Documentation
¶
Overview ¶
Package vault stores the credentials agents need, encrypted at rest, with every reveal audited.
Two design choices are deliberate departures from cabrain's vault, which was the obvious thing to reuse:
- Reveal is its own grant. cabrain requires WRITE access on a brain to reveal a secret, so a read-only agent cannot read a credential — the permission is backwards. Here can_reveal is separate from can_list and from any write capability.
- Every reveal writes an audit row in the same transaction as the decrypt, or the reveal does not happen. cabrain emits only a transient event, so there is no durable answer to "who read this key?".
Index ¶
- Constants
- Variables
- func AAD(scope, agentSlug, name string) string
- func Hint(plaintext string) string
- func ValidateVaultKey(raw string) error
- type Service
- type Store
- func (s *Store) HasSecret(ctx context.Context, name string) bool
- func (s *Store) PutSystem(ctx context.Context, name, value, reason string) error
- func (s *Store) RevealFor(ctx context.Context, name, agentSlug, runID string) (string, error)
- func (s *Store) RevealSystem(ctx context.Context, name, reason string) (string, error)
- func (s *Store) Routes(r chi.Router)
- type SystemReader
Constants ¶
const KeySize = 32
KeySize is the AES-256 key length in bytes.
Variables ¶
var ErrNoKey = errors.New("BUILDER_VAULT_KEY is not set")
ErrNoKey is returned when BUILDER_VAULT_KEY is absent.
Functions ¶
func ValidateVaultKey ¶
ValidateVaultKey reports whether raw is a usable 32-byte key.
Accepts standard or URL-safe base64, with or without padding, or 64 hex characters — because operators generate these with whichever of `openssl rand -base64 32` or `openssl rand -hex 32` they remember.
Types ¶
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service is the vault.
func New ¶
New builds the vault from BUILDER_VAULT_KEY.
A missing or malformed key is fatal at boot rather than at first use: an app that starts with a broken vault fails later, inside an agent run, where the cause is far harder to see.
func (*Service) Open ¶
Open decrypts a stored ciphertext. aad must match exactly what Seal was given.
func (*Service) Seal ¶
Seal encrypts plaintext under the row's identity.
aad binds the ciphertext to the row it belongs to (scope:agent_slug:name). AES-GCM authenticates it, so a ciphertext copied into a different row fails to decrypt — which makes row-swapping a non-attack rather than a privilege escalation.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is the vault's persistence + audit layer.
func (*Store) HasSecret ¶ added in v0.3.0
HasSecret reports whether a named secret exists, without decrypting it.
The gallery needs to answer "is this provider configured?" on every page load. Doing that with RevealSystem would decrypt a client secret, and write an audit row, purely to decide whether to enable a button — a read that happens dozens of times a day and never uses the value.
func (*Store) PutSystem ¶ added in v0.3.0
PutSystem stores (or rotates) a secret the installation owns.
The write half of RevealSystem, and it exists for the same reason: an OAuth client secret is configuration the server holds, not a credential granted to an agent, so it has no agent to attribute the write to.
Rotation semantics match handleStore exactly — the previous version stays, marked not-current — because "what was live when this ran?" has to stay answerable after somebody replaces a key.
func (*Store) RevealFor ¶
RevealFor is the in-process path agents use, bypassing HTTP. Same contract: the audit row and the decrypt share a transaction.
func (*Store) RevealSystem ¶ added in v0.3.0
RevealSystem decrypts a secret held by the installation itself.
`reason` names the subsystem asking and lands in the audit row. It is required — an unattributed system read is exactly the thing this design is trying not to become.