SPEC-122 v0.2 - Prism-Vault Shared Config and Secrets Service
Status: draft
Owner / Author: Texi (architecture)
Phase name: Prism-Vault
First consumer: Prism
Later consumers: Janus, DPA-Crawl/WebCrawl, and other projects after the Prism-first rollout proves the contract.
Related: SPEC-019 (local/lan/cloud modes), SPEC-056 (isolation), SPEC-087 (Render deployment), SPEC-089 (cloud auth), SPEC-116 (bridge audit pattern), Datris datris-platform-oss HashiCorp Vault implementation review 2026-05-14.
Summary
Prism-Vault is a Prism-first shared config, secret, and credential-resolution
service. It gives Prism agents and Prism backend surfaces one stable contract for
runtime configuration and secret access across local, LAN, and Render production
deployments.
The service lives as a Prism-accessible separable subproject for this phase, not
as a separate repository immediately. It still carries its own
version/build/deploy lifecycle from the start. That boundary is deliberate:
Prism-Vault begins under the Prism umbrella for easier build/test/debug and
integration, while remaining cleanly peelable into a standalone service later.
Janus and DPA-Crawl/WebCrawl are explicitly later collaboration targets. They
inform the design, but they are not the first integration or acceptance anchor.
The first rollout proves Prism-scoped secret/config management for Prism agents.
Datris is useful prior art: it has a compact Vault KV v2 wrapper,
self-describing secret records, environment-scoped paths, UI masking,
masked-placeholder preservation, and runtime secret injection into worker
scripts. Prism-Vault keeps those ergonomic lessons but hardens the model for
Prism production use and later multi-project expansion.
v0.2 Ratification Amendment - 2026-05-18
This amendment prepares SPEC-122 for Frank ratification after the initial
settings-backed skeleton landed and Carla became unavailable.
V-0 build-start gate. No new Prism-Vault implementation code may land after
the existing skeleton until Frank ratifies this SPEC-122 v0.2 shape plus the
scoped Plan #28 update. Operations Console remains the active implementation
lane; Vault work queues behind it.
Phase 0A - existing contract skeleton. The current implementation is
recognized as Phase 0A only:
backend/app/schemas/vault.py
backend/app/services/vault_service.py
backend/app/routers/vault.py
backend/tests/test_vault_router.py
- Prism-Vault docs, diagrams, and Plan #28 surfaces
Phase 0A establishes the HTTP schema/router/service seam, a
SettingsBackedVaultProvider, masked metadata reads, runtime resolve response
shape, config get, service-scope denial, header-bound audit caller identity,
agent run_ref requirement, stub audit IDs, and no-raw-value error tests. It is
not a full durable Vault implementation.
Phase 0A explicit non-claims. Phase 0A does not claim:
- durable
prism_vault_* SQL tables,
- persistent append-only audit,
- real Vault KV v2 provider behavior,
- Render provider behavior,
- production secret ownership migration,
- full secret/config CRUD,
- MCP-side
prism_vault_* verb parity,
- operator UI,
- rotation/revocation automation.
Phase 0B - durable audit and policy floor. The next build slice after
ratification is durable audit:
- Alembic migration and model for append-only Vault audit events.
- Service audit sink that writes an attempt/reservation before any provider read.
- Success, denied, and failure finalization before any raw value returns.
- Fail-closed
prism_vault_audit_unavailable behavior when audit cannot be
written.
- Provider-neutral tests for success, denied, missing, and audit-unavailable
paths.
Raw secret values remain out of Prism SQL. Audit stores hashes and metadata
only.
Phase 0C - agent verb parity. After Phase 0B, add thin MCP verb shims that
round-trip to the HTTP source contract:
prism_vault_secret_get_metadata
prism_vault_secret_resolve
prism_vault_config_get
Phase 0C includes an MCP smoke proving the verbs call /vault; no shim-only
stub is acceptable.
Phase 1 - LAN-first real provider. Because server1 already has a Vault
container, the first real provider is LAN Vault KV v2:
- non-root app token,
- scoped Vault policy,
- readiness/policy probe,
- provider selection that keeps caller APIs provider-neutral,
- same contract tests against the LAN provider.
Local parity follows as Phase 2. Render remains deferred unless Frank explicitly
reorders.
Carla coverage. Carla remains the eventual viewer/review coordinator but is
offline and not an active blocker. Ratification and implementation coordination
route through Frank, Donna, Texi, Candi, Samantha, Lafonda, and Desiree per Plan
#28.
Diagrams
- Architecture:
docs/diagrams/prism-vault/shared-service-architecture.mmd
- Runtime resolution:
docs/diagrams/prism-vault/secret-resolution-sequence.mmd
- Deployment modes:
docs/diagrams/prism-vault/deployment-modes.mmd
- Scope data model:
docs/diagrams/prism-vault/scope-data-model.mmd
Problem
Prism needs a first-class service for secrets and runtime config before extending
the pattern to Janus/DPA-Crawl or other projects. Current deployment modes have
different ways to pass credentials and config:
- Local mode often uses
.env and dev-service defaults.
- LAN mode needs durable, non-root secret handling with private-network access.
- Render production uses platform env/secret bindings and may need an external
provider for per-project runtime secrets.
- Agents need scoped runtime values without broad secret-store credentials.
- Operators need masked metadata, audit evidence, and repairable config state.
The Datris implementation proves the developer ergonomics, but its app-local
Vault shape is not the final Prism service boundary. Prism-Vault makes that
boundary explicit.
Goals
- Deliver Prism-scoped secret/config management first.
- Support Prism agents and backend services as first-class runtime consumers.
- Support local, LAN, and Render deployments behind one Prism-Vault contract.
- Keep the provider-adapter design so Vault, Render, or a future managed
provider can back the same API.
- Enforce tenant/project/environment/service scope in the backend.
- Return masked metadata on admin/read paths and reserve raw secret values for
runtime resolution only.
- Capture audit rows sufficient to answer who resolved what, for which Prism
project, purpose, and run/job/session.
- Establish subproject-first Prism-Vault version/build/deploy boundaries from
day one, even while it lives under the Prism umbrella.
- Defer Janus/DPA-Crawl/WebCrawl integration until the Prism path is stable.
Non-Goals
- Making DPA-Crawl/WebCrawl the first consumer.
- Replacing Prism’s existing human auth system. Prism-Vault consumes
authoritative caller identity; it does not become the IdP.
- Cross-tenant secret sharing in v0.1.
- Direct per-agent or per-worker Vault tokens in v0.1. Runtime consumers receive
resolved env payloads, not provider credentials.
- Building a full human-facing admin console in this spec. Console screens can
consume the API but are separate implementation work.
- Provider-specific rotation automation for every possible secret kind. v0.1
defines monthly and manual-on-demand rotation policy plus hooks/audit;
individual automated rotators land incrementally.
Architecture Decision
Prism-Vault is a broker with three internal surfaces:
- Secret Store Surface - CRUD and metadata for secret records.
- Config Store Surface - versioned non-secret Prism runtime config.
- Credential Resolver Surface - runtime-only resolution for Prism agents,
backend services, and later worker integrations.
All three surfaces pass through the same identity, scope, policy, and audit gate.
The provider adapter is behind that gate. Callers do not talk directly to Vault,
Render environment variables, or any future provider.
Subproject Boundary
Prism-Vault starts as a Prism-accessible separable subproject inside the Prism
umbrella. Do not require a separate GitHub repository in v0.1. The subproject
must still behave like an independent service:
- Own separable subproject path, with repo-separation-ready boundaries.
- Own version string and changelog.
- Own build artifact or image.
- Own deploy config for local, LAN, and Render.
- Own contract tests.
- No hard dependency on Prism monolith internals for provider adapters.
- Prism integration goes through the Prism-Vault API, not direct table/provider
access.
This keeps future extraction boring: move the subproject into a separate repo or
deploy unit later, preserve the API.
Phase 0 Boundary Concretization
Phase 0 must create the separable subproject boundary in one concrete place so
implementation slices do not each invent their own shape:
- Code root / package boundary: Prism-Vault runtime code lands under the
existing Prism repo at
backend/app/{routers,schemas,services}/vault.py.
Phase 0C adds mcp-node/src/verbs/vault.ts as the Prism-hosted agent surface.
The extraction-ready subproject anchor remains prism-vault/, which holds
the independent version line and README contract for later peel-off.
- Version source:
prism-vault/VERSION is the source of truth for the
subproject version. Prism repo/package versions must not silently substitute
for the Prism-Vault line.
- Image / artifact name: the intended extraction-ready artifact name is
prism-vault. Until extraction, any Prism-hosted build artifact or container
image for this surface should carry that name in tags/labels rather than a
Janus or generic backend label.
- Deploy config location: Prism-hosted deploy wiring lives in the normal
Prism deploy surfaces (
docker-compose*.yml, render.yaml, and related
deploy/runbooks) with Prism-Vault-specific entries called out explicitly as
the future service boundary.
- Contract-test target: Phase 0A contract tests target the Prism-hosted
/vault HTTP routes at backend/tests/test_vault_router.py. Phase 0C adds
matching MCP-side smoke coverage for the prism_vault_* verb surface.
This is intentionally Prism-hosted in v0.2, but the names above are the
canonical boundary implementers must use so later extraction is mechanical.
Scope Model
Canonical scope is:
tenant_id / project_id / environment / service / record_name
Human-readable slugs may be accepted as lookup input, but authorization uses
stable IDs. Example logical scopes:
f4b93fa7 / PID-PGR01 / local / prism / openai-api-key
f4b93fa7 / PID-PGR01 / lan / prism-agent / github-token
f4b93fa7 / PID-PGR01 / render / prism-cloud / workos-secret
Later Janus/DPA-Crawl examples are valid only after the later integration phase:
f4b93fa7 / PID-DPA01 / lan / webcrawl / browserbase-key
Environment names are deployment/runtime scopes, not Git branches. Required
initial values:
Additional environments such as staging, preview, or customer-prod require
explicit registration and policy.
Provider Binding
Local
Local mode MAY run Vault dev mode for fast setup. Local mode MAY read bootstrap
inputs from .env, but .env is an input channel only. Prism-Vault state and
audit must still be written through Prism-Vault.
LAN
LAN mode uses a real Vault deployment by default:
- non-root app token,
- project-scoped policies,
- durable storage,
- normal seal/unseal operation,
- private LAN address,
- no public Vault port exposure.
Render
Render mode uses the same Prism-Vault API contract. Prism-Vault remains the
primary vault and service of record. Render-native environment variables and
secret bindings are exceptions for Render services that must consume values
through native Render service boundaries. Runtime/project secret records should
default to Prism-Vault unless a Render boundary makes native binding required.
Render is not allowed to leak into caller APIs. Callers still call
prism_vault_secret_resolve, prism_vault_secret_put,
prism_vault_config_list, etc. The adapter decides whether the provider read
comes from Prism-Vault provider storage, Render env/secret binding, or external
Vault.
Provider credential isolation
Provider adapter credentials are deployment-mode-local implementation detail, not
shared application config. LAN Vault app tokens, Render-native bindings, and any
future managed-provider credentials must be loaded exclusively by their own
adapter, scoped to the active deployment mode, and must never appear in a
unified config namespace that another mode, caller, or adapter can read.
API Contract
v0.2 follows the normal Prism capability shape:
backend service -> /vault HTTP router -> thin mcp-node prism_vault_* verb shims
Do not add a standalone Prism-Vault SDK in v0.2. The backend HTTP API is the
source contract. mcp-node verbs are thin clients for agents and land in Phase
0C after durable audit exists. A separate SDK may be extracted later only when a
non-MCP consumer exists, such as a CI tool or deploy script.
Names below are logical verb semantics that the HTTP routes expose and the Phase
0C mcp-node shims must mirror.
prism_vault_secret_put(scope, name, fields, labels?, policy?)
prism_vault_secret_get_metadata(scope, name)
prism_vault_secret_list(scope, filters?)
prism_vault_secret_delete(scope, name)
prism_vault_secret_rotate(scope, name, rotator_ref?)
get_metadata and list never return raw secret values. Sensitive fields are
masked and include only field names, last updated metadata, provider ref, labels,
and policy summary.
Runtime resolution
prism_vault_secret_resolve(scope, name, purpose, caller, run_ref?, field_allowlist?)
Resolution is the only v0.1 path that returns secret values. It is allowed only
for runtime identities such as a registered Prism agent or backend service. It
must include a purpose.
purpose is a registered string, not free text. Resolution must validate
purpose against an allowlist or registry before policy evaluation. wildcard or
prefix-based purpose matching is forbidden by default and is allowed only when a
record policy explicitly opts into that broader match shape.
The response is a short-lived env payload:
{
"ttl_s": 900,
"env": {
"OPENAI_API_KEY": "...",
"GITHUB_TOKEN": "..."
},
"audit_id": "..."
}
The TTL is the maximum post-resolution exposure window. Rotation or revocation
does not retroactively invalidate an already-issued env payload unless an
emergency revocation path explicitly disables it. v0.1 must document and support
an emergency revocation path independent of normal TTL expiry, such as ttl_s=0
responses or provider-level disablement.
Config
prism_vault_config_put(scope, name, document, schema_ref?, labels?)
prism_vault_config_get(scope, name, version?)
prism_vault_config_list(scope, filters?)
prism_vault_config_promote(scope, name, from_version, to_environment)
Config records are non-secret JSON documents. They are versioned and may be
returned to authorized callers. Examples: model routing defaults, provider
selection, agent runtime knobs, allowed outbound integrations, cloud deploy
config, and service health thresholds.
Config Store is non-secret by contract. Writes must reject, quarantine, or fail
review on secret-looking fields/values where practical, and examples/docs must
route credentials, tokens, API keys, client secrets, passwords, and similar
sensitive material through Secret Store instead of Config Store. Config Store is
not an escape hatch around masking, audit, or resolution discipline.
Authorization Contract
Backend authorization is enforced, not advisory.
Every request must resolve:
caller_identity
caller_kind (human, agent, service)
tenant_id
- requested
project_id
- requested
environment
purpose
Default rule: a caller can only read, write, list, or resolve inside its own
tenant and project. Cross-project access is denied unless an explicit policy
grants the caller and purpose.
For v0.1, shared-service access is Prism-first:
- Prism agents may resolve only Prism-scoped records that match their project,
identity, and purpose.
- Prism backend services may manage service-scoped records through service
identity.
- Operator/admin identities may write and rotate records within their tenant.
- Janus/DPA-Crawl and non-Prism projects have no default access in v0.1.
Policy write/modify operations are restricted to caller_kind in {human, service} and are explicitly denied to agent and worker callers. An agent or
worker may not grant itself broader resolve access by attaching or mutating
policy data in a secret/config write path.
If Prism-Vault is uncertain whether an operation is allowed, it fails closed and
writes a denied audit event.
Prism Integration Contract
Prism is the first integration target. The initial Prism rollout must support:
- Prism backend service secrets for local/LAN/Render.
- Prism agent runtime secret resolution for explicitly allowed purposes.
- Prism config records for non-secret runtime config.
- Masked metadata lookup for operator/admin inspection.
- Audit lookup scoped to the requesting Prism project.
Required resolve input:
{
"scope": {
"tenant_id": "...",
"project_id": "PID-PGR01",
"environment": "lan",
"service": "prism-agent"
},
"name": "agent-provider-credentials",
"purpose": "prism.agent.execute",
"run_ref": "session_or_scheduled_run_id",
"field_allowlist": ["OPENAI_API_KEY"]
}
Agents must not receive broad Prism-Vault admin credentials. They receive
resolved env payloads only. Agent/tool logs must mask every resolved value before
persistence.
Later Janus / DPA-Crawl Contract
Janus and DPA-Crawl/WebCrawl remain design targets but move near the end of the
rollout. Their integration should happen only after Prism local, LAN, and Render
paths pass the same contract tests.
Later DPA-Crawl/WebCrawl integration will:
- Read versioned crawl/extraction config before starting a job.
- Resolve only the credentials that job needs immediately before execution.
- Require
job_id for worker resolves.
- Mask resolved values in worker logs before persistence.
That later phase may add caller_kind=worker and job-specific policy rules.
Data Model
Minimum durable records:
prism_vault_secret_records
id
tenant_id
project_id
environment
service
name
provider
provider_ref
labels
active
created_at
updated_at
prism_vault_secret_versions
id
secret_record_id
version
field_names
value_hash
provider_version_ref
created_by
created_at
Raw values stay in the provider, not in Prism SQL, unless a future encrypted
storage spec explicitly creates a Prism-Vault-native provider.
prism_vault_config_records
id
tenant_id
project_id
environment
service
name
schema_ref
active_version
created_at
updated_at
prism_vault_config_versions
id
config_record_id
version
document_json
document_hash
created_by
created_at
prism_vault_access_policies
id
tenant_id
project_id
environment
service
record_ref
caller_kind
caller_identity_ref
purpose
permission
effect
prism_vault_audit_events
id
tenant_id
project_id
environment
service
record_name
record_kind
operation
caller_identity
caller_kind
purpose
run_ref
provider
result
value_hash
created_at
Audit never stores raw secret values.
Audit rows are append-only at the application layer: no UPDATE or DELETE path is
allowed for runtime callers or admin surfaces. v0.1 requires a minimum 12-month
retention period for audit evidence. A future integrity layer, such as chained
hashes or append-only secondary export, must be named even if Phase 0 defers its
implementation.
Datris Lessons Adopted
Prism-Vault SHOULD adopt these Datris patterns:
- Self-describing secret payloads for complex provider slots.
- Masked metadata on read paths.
- Preserve existing sensitive field values when a UI sends the masked
placeholder.
- Hot reload for config/AI/provider changes where the consuming service supports
it.
- Runtime env injection, with log masking before persistence.
Prism-Vault MUST improve these areas:
- No fail-open tenant fallback. Unmapped or missing caller credentials fail
closed.
- No root-token provider access in LAN or Render.
- No stack traces returned to clients.
- No direct broad provider reads from application code.
- No caller-visible dependency on
.env persistence in production.
Security Requirements
- All state-changing writes require authenticated caller identity.
- Raw secret values are returned only by
prism_vault_secret_resolve.
prism_vault_secret_resolve requires purpose.
- Prism project isolation is default-deny.
- Cross-project policies require explicit operator/admin creation and audit.
- Provider adapter credentials are service-owned and never sent to agents.
- Provider adapter credentials are adapter-local and mode-scoped; local callers
cannot observe LAN or Render adapter credentials, and vice versa.
- Every value resolution writes an audit event before returning the value.
resolve() MUST create an audit attempt/reservation before provider read and
MUST finalize success, denied, or failure before returning any value.
- Audit writes are atomic with Prism-Vault authorization. If audit finalization
cannot be written, no value is returned and the operation fails closed.
- Audit evidence is append-only and retained for at least 12 months.
- Runtime resolve responses remain provider-neutral: callers receive
ttl_s,
env, and audit_id, not provider refs or provider version refs.
- Policy write/modify is denied to
agent and worker callers.
purpose values are validated against a registered allowlist; wildcard/prefix
policies require explicit per-record opt-in.
- Logs must mask exact resolved values and common transformed forms where
practical.
- Render production defaults to Prism-Vault as service of record. Render-native
encrypted env/secret handling is allowed only where the Render service boundary
requires it; local
.env is not production state.
Rotation Policy
v0.1 baseline rotation cadence:
- Monthly scheduled rotation for Prism-Vault-managed production secrets where the
provider supports rotation.
- Manual on-demand rotation whenever an operator requests it, a credential is
suspected exposed, a user/service leaves scope, or a provider key must be
replaced.
- Every rotation attempt writes audit: requested_by, reason, record scope,
provider, old value hash, new value hash, result, and timestamp.
- Rotation automation may be partial in v0.1, but the policy, audit shape, and
manual operation path must exist.
Failure Modes
| Name | Meaning | Handling |
|---|
prism_vault_scope_denied | Caller lacks permission for requested scope | 403, denied audit |
prism_vault_provider_unavailable | Backing provider cannot be reached | 503, no fallback to stale secret unless policy says cached metadata only |
prism_vault_secret_missing | Record or provider value missing | 404, audit |
prism_vault_audit_unavailable | Audit write failed | fail closed |
prism_vault_render_binding_missing | Render adapter expected env binding absent | 503 in production, setup error in deploy smoke |
prism_vault_run_context_missing | Runtime resolve omitted required run_ref for a policy that requires it | 400 |
Implementation Plan
Phase 0A - Settings-backed service boundary skeleton
- Land this spec and Mermaid diagrams.
- Create the Prism-Vault separable subproject boundary and independent
version/build plan inside the Prism umbrella.
- Define
vault_service.py, /vault HTTP router, schemas, settings-backed
provider, and router tests.
- Prove masked metadata, runtime resolve shape, config get, service-scope
denial, header-bound audit caller, agent
run_ref requirement, audit
fail-closed seam, and no raw value in error paths.
- Do not introduce durable
prism_vault_* tables or real provider behavior.
Phase 0B - Durable audit and policy floor
- Add append-only Vault audit migration/model/sink.
- Require audit attempt/reservation before provider read.
- Finalize success, denial, and failure audit before returning.
- Fail closed if audit is unavailable.
- Keep raw secret values out of Prism SQL.
- Add provider-neutral tests for audit success, denied, missing, and
audit-unavailable paths.
Phase 0C - MCP verb parity
- Add thin
mcp-node shims for prism_vault_secret_get_metadata,
prism_vault_secret_resolve, and prism_vault_config_get.
- Add MCP smoke proving the shims call the
/vault HTTP source contract.
- Do not add a standalone SDK in v0.1.
Phase 1 - Prism LAN provider
- Implement Vault KV v2 adapter for LAN mode.
- Add non-root app token and scoped policy.
- Add health/readiness probe for Vault availability and policy identity.
- Prove Prism agents and Prism backend services resolve only Prism-scoped
records without cross-project leakage.
Phase 2 - Prism local provider
- Implement Vault KV v2 adapter for local mode.
- Implement Prism local bootstrap for Prism-scoped config and secrets.
- Treat
.env as seed input only; state and audit still go through
Prism-Vault.
- Prove Prism can put metadata, get masked metadata, resolve for a Prism runtime
purpose, and observe audit.
Phase 3 - Prism Render provider
- Add Render adapter for service-level env/secret bindings.
- Define Render deploy env contract and smoke probes.
- Prove Render deployment serves the same Prism-Vault API contract as local/LAN.
- Make Prism-Vault the default service of record in Render.
- Use Render-native env/secret bindings only where the Render service boundary
truly requires native injection; document each exception.
Phase 4 - Prism operations
- Add operator UI/API for masked metadata, config version history, promotion,
policy creation, and audit lookup.
- Add monthly and manual-on-demand rotation hooks for common Prism provider keys.
- Add export/import for Prism project migration between local, LAN, and Render.
Phase 5 - Janus and DPA-Crawl collaboration
- Add Janus/DPA-Crawl as later consumers after Prism paths are stable.
- Add worker caller kind and job-scoped policy gates.
- Prove WebCrawl job credential resolution with
job_id.
- Keep non-Prism integration behind explicit policy and audit.
Acceptance Criteria
- Prism can store masked metadata for Prism-scoped records.
- Prism agents can resolve an allowed Prism runtime secret and cannot resolve it
outside the allowed purpose/scope.
- Prism backend services can consume Prism-Vault config in local, LAN, and
Render modes.
- Local and LAN pass the same Prism-Vault contract tests against different
providers.
- Render passes the same API contract, with provider-specific differences hidden
behind the adapter.
- Masked metadata read never returns raw values.
- Every resolve emits an audit event with caller, scope, purpose, run_ref when
present, provider, result, and value hash.
- Missing or unmapped tenant/project identity fails closed.
- Prism-Vault has separable subproject version/build/deploy metadata sufficient
for later extraction from the Prism umbrella.
- Render mode treats Prism-Vault as the default service of record, with
Render-native secret bindings documented as explicit exceptions.
- Monthly and manual-on-demand rotation policy is documented and audited.
- Provider adapter credentials remain isolated by deployment mode and are not
exposed through runtime config or cross-mode resolution.
- Agent/worker callers cannot create or modify access policies.
- Purpose validation rejects unregistered purpose strings unless an explicit
per-record wildcard rule exists.
- Audit evidence is append-only, retained for at least 12 months, and created
before any resolved value is returned.
- Rotation/revocation docs explicitly state TTL as the max exposure window and
document an emergency revocation path independent of normal TTL expiry.
- Janus/DPA-Crawl acceptance is deferred to Phase 5 and is not a v0.2 ship gate.
Ownership
- Architecture: Texi
- First consumer: Prism
- Prism backend integration: Donna / Prism backend owner
- Install/deploy: Lafonda
- Governance/security review: Candi
- Viewer/review coordination: Carla
- Later collaboration: Janus / DPA-Crawl after Prism rollout gates pass
Last modified on May 18, 2026