@agentproto/secrets
v0.2.0
Published
@agentproto/secrets — AIP-19 SECRETS.md reference implementation. A workspace-level manifest format for declaring secret slugs, their purpose, access grants, and audit metadata — without ever storing the values themselves. Hosts resolve slugs against a re
Maintainers
Readme
@agentproto/secrets
AIP-19 SECRETS.md reference implementation. A workspace-level manifest format for declaring secret slugs, their purpose, access grants, and audit metadata — without ever storing the values themselves. Hosts resolve slugs against a real vault at reveal time.
Status: 0.1.0-alpha. Generated by
scripts/scaffold-aip.mjs—build()andvalidate()bodies are TODOs.
Spec: https://agentproto.sh/docs/aip-19
Usage
import { defineSecrets } from "@agentproto/secrets"
const x = defineSecrets({
id: "my-secrets",
description: "Short purpose.",
// ...
})Exposure surface (@agentproto/secrets/exposure)
The manifest says which secrets exist; an exposure says how a resolved
secret reaches a runtime. SecretExposure is a discriminated union — one
kind per delivery mechanism, so consumers filter with the isExposureKind
guard and handle each with one switch case:
| kind | Delivers the secret as… |
| --- | --- |
| env | an environment variable on the agent process |
| file | a file written into the workspace |
| egress-substitute | a $$SECRET[NAME]$$ placeholder substituted on egress |
| mcp-header | an HTTP auth header on an MCP server's transport |
import { isExposureKind, type SecretExposure } from "@agentproto/secrets/exposure"
const headerExposures = exposures.filter(e => isExposureKind(e, "mcp-header"))mcp-header — brokered MCP auth
interface McpHeaderExposure {
kind: "mcp-header"
credentialPath: string // broker path: "<providerId>" or "<providerId>/<account>"
server?: string // optional upstream override, forwarded to the broker
}resolveMcpHeaderExposure(exposure, resolver, { signal }) turns that path into
a ready-to-use header map. The resolver is a structural
McpHeaderResolver (resolveHeaders({ path, server?, signal? })) — the
CredentialBroker from @agentproto/auth satisfies it with no adapter, so
this package stays dependency-free of auth while still driving it.
Every resolved header value is run through assertSafeSecretValue before
return — a value containing CR/LF/NUL is rejected, closing off header-injection
(a smuggled newline can't append extra header lines onto the transport).
import { resolveMcpHeaderExposure } from "@agentproto/secrets/exposure"
import { CredentialBroker } from "@agentproto/auth"
const headers = await resolveMcpHeaderExposure(exposure, broker, { signal })
// → { Authorization: "Bearer …" }, guardedThe same guard (assertSafeSecretValue) and the $$SECRET[NAME]$$ engine
(substituteSecrets, SECRET_PLACEHOLDER_PATTERN, formatPlaceholder) are
exported from this subpath for the egress-substitute path.
License
MIT — see LICENSE.
