secret-manager stores encrypted application secrets and exposes them to
authenticated users and explicitly authorized plugins. It is a WASM plugin,
so it uses the same plugin contract and host callbacks as other WASM plugins.
Plugin identity
The plugin name is used in the plugin configuration, as the target of plugin
messages, and in the access policy for individual secrets.
Configuration
The standard plugin settings are configured under[Plugins.secret-manager]:
Params are passed to the plugin in its RegisterRequest. The plugin uses
the following parameter names:
The plugin persists changes to its own explicit
Params section. It does not
write these values to the shared KV store. The host applies a parameter patch
to the current configuration and refreshes the plugin manager’s configuration
snapshot; the plugin also updates its in-memory copy.
Treat secretmgr.identity and all secretmgr.secret.* values as sensitive.
The identity is a private age key, and the secret parameters contain encrypted
secret material that should not be exposed in logs or ordinary configuration
diagnostics.
Supplying the identity through the environment
The identity parameter supports the Kernel’s environment reference syntax. To keep the private key out of the configuration file, store the value in an environment variable and reference it fromParams:
env://SECRET_KEY before passing Params to the plugin.
The reference without a trailing ? is required: if SECRET_KEY is not set,
parameter resolution fails and secret-manager cannot start. This is
preferable to silently generating a new identity when encrypted data already
exists.
On a first installation with no secretmgr.identity parameter, the plugin
generates an age X25519 identity and persists it through PatchParams. You can
then move that generated identity to a protected environment variable, set
"secretmgr.identity" = "env://SECRET_KEY", and start the application with
SECRET_KEY present. Keep using the same identity: an identity-encrypted
secret cannot be decrypted with a newly generated key.
If the identity must be stored directly in the configuration, the individual
secret values can still be protected with scrypt encryption and a
passphrase, so the configuration contains ciphertext rather than plaintext
secret values. The current plugin does not accept a passphrase-wrapped value
for secretmgr.identity itself; that parameter must resolve to the raw age
identity. The identity is still required to decrypt identity-encrypted
secrets.
Secret metadata
Eachsecretmgr.meta.<name> value has this shape:
encryption is either identity or scrypt. The allowed_plugins and
updated_at fields are also maintained in the metadata returned by the HTTP
API. Plugin names in the policy are compared with the authenticated source
name from the Kernel.
Secret names
Secret names must be 1–128 characters after trimming. They may contain ASCII letters, digits,/, _, -, and ., but may not contain ... Empty names
and names containing any other character are rejected.
Encryption
The plugin stores age ciphertext encoded with standard Base64.
The passphrase is never persisted. A forgotten passphrase makes an
scrypt
secret unrecoverable. Scrypt derives the encryption key from the passphrase
with a deliberately expensive, memory-hard computation. This makes each
offline password guess more costly, which is why passphrase-based encryption
is slower than using the stored X25519 identity. Choose it when the additional
delay is acceptable. Both the authenticated HTTP API and the plugin-to-plugin
IPC interface can retrieve scrypt secrets when the caller supplies the
passphrase.
Without the correct secretmgr.identity, an identity-encrypted secret
cannot be decrypted. The ciphertext is not sufficient to recover it.
The maximum plaintext size accepted during decryption is 1 MiB.
HTTP API
All registered HTTP routes require an authenticated user. The plugin returns JSON withContent-Type: application/json; charset=utf-8 and
Cache-Control: no-store.
List secrets
Add a secret
passphrase is optional. An empty value selects identity encryption. A
non-empty value selects scrypt encryption. allowed_plugins may be empty,
which means that no plugin can retrieve the secret through IPC.
Success returns HTTP 201 Created:
400 Bad Request for malformed JSON, an invalid name, or
an invalid plugin policy, 409 Conflict when the secret already exists, and
500 Internal Server Error when encryption or persistence fails.
Update a secret
/keys/add. The name identifies the
existing secret and cannot be changed. To keep the existing ciphertext while
changing metadata or the access policy, set value to the single character
* and leave passphrase empty. Supplying a passphrase with this sentinel is
rejected.
Success returns HTTP 200 OK with the same { "success": true, "name": "..." } shape. An unknown secret returns 404 Not Found; malformed input or
an invalid policy returns 400 Bad Request.
Reveal a secret
scrypt encryption and must
be empty for an identity secret. Success returns the plaintext:
400 Bad Request for malformed input or a missing or
invalid scrypt passphrase. A missing secret or other decryption failure is
returned as 404 Not Found.
Delete a secret
200 OK:
400 Bad Request.
Plugin message API
Plugin-to-plugin retrieval uses the Kernel’s synchronous plugin message transport. The target plugin name issecret-manager, and the supported topic
is:
allowed_plugins for the requested
secret. The passphrase may be empty for an identity secret and is required
for an scrypt secret. The Kernel sets the message source to the registered
name of the sender; a caller cannot spoof this field.
On success, the reply places the plaintext in message:
error and does not return a
secret value:
scrypt passphrases, and invalid passphrases.
For the common message envelope and host-authenticated source behavior, see
the plugin message IPC guide.
KV and other plugin interfaces
secret-manager does not use the shared KV store for secret data. Consumers
must not look for secrets in a KV namespace; use the HTTP API or the plugin
message topic described above.
The plugin does not declare a Socket.IO namespace or events. Its host
interaction is limited to:
- receiving
Paramsduring registration; - using the host
PatchParamscallback to persist identity and secret changes; - handling authenticated HTTP requests;
- receiving authenticated plugin messages.
Security considerations
- Use HTTP endpoints only for authenticated administrative access.
- Prefer plugin messages for service-to-service access, and grant each secret only to the plugin names that need it.
- Treat plugin message replies as plaintext secret material.
- Supply the passphrase for every
scryptretrieval, and never log or persist it in the calling plugin. - Do not log request bodies, passphrases, decrypted values, identities, or ciphertext parameters.