Documentation
¶
Index ¶
- Constants
- Variables
- func BuildPayloadMeta(payload map[string]any, tpl Template) map[string]any
- func DecryptLegacyValue(ciphertextHex string) (string, error)
- func DecryptPayload(encrypted string) (map[string]any, error)
- func EncryptPayload(payload map[string]any) (string, error)
- func EnsureRuntimeKey(dataDir string) (warning string, generated bool, err error)
- func ExtractSecretID(v string) (string, bool)
- func FirstStringFromPayload(payload map[string]any, keys ...string) string
- func IsAllowedAccessMode(mode string) bool
- func IsSecretRef(v string) bool
- func LoadKeyFromEnv() error
- func LoadTemplatesFromDefaultPath() error
- func LoadTemplatesFromFile(path string) error
- func NormalizeAccessMode(mode string) string
- func ReadSystemSingleValue(secret *Secret) (string, error)
- func RegisterHooks(app *pocketbase.PocketBase)
- func ValidatePayload(payload map[string]any, tpl Template) error
- func ValidatePolicy(raw map[string]any) map[string]string
- func ValidateRef(app core.App, secretID, userID string) error
- type Policy
- type ResolveError
- type ResolveResult
- type RevealResult
- type Secret
- func FindSystemSecretByNameAndType(app core.App, name, secretType string) (*Secret, error)
- func From(rec *core.Record) *Secret
- func UpsertSystemPayloadSecret(app core.App, secret *Secret, name, templateID string, payload map[string]any) (*Secret, error)
- func UpsertSystemSingleValue(app core.App, secret *Secret, name, secretType, plaintext string) (*Secret, error)
- func (s *Secret) AccessMode() string
- func (s *Secret) CanBindByUser(userID string) bool
- func (s *Secret) CanReveal() bool
- func (s *Secret) CreatedBy() string
- func (s *Secret) ExpiresAt() time.Time
- func (s *Secret) ID() string
- func (s *Secret) IsExpired() bool
- func (s *Secret) IsExpiringSoon(warnDays int) bool
- func (s *Secret) IsOwnedBy(auth *core.Record) bool
- func (s *Secret) IsRevoked() bool
- func (s *Secret) IsSystemManaged() bool
- func (s *Secret) Name() string
- func (s *Secret) Record() *core.Record
- func (s *Secret) Scope() string
- func (s *Secret) SetAccessMode(mode string)
- func (s *Secret) Status() string
- func (s *Secret) TemplateID() string
- func (s *Secret) Version() int
- type Template
- type TemplateField
Constants ¶
const ( SettingsModule = "secrets" PolicySettingsKey = "policy" AccessModeUseOnly = "use_only" AccessModeRevealOnce = "reveal_once" AccessModeRevealAllowed = "reveal_allowed" )
const ( ReasonNotFound = "secret not found" ReasonRevoked = "secret has been revoked" ReasonExpired = "secret has expired" ReasonAccessDenied = "access denied: secret is private to another user" )
Machine-readable reason strings for ResolveError.
const ( StatusActive = "active" StatusRevoked = "revoked" ScopeGlobal = "global" ScopeUserPrivate = "user_private" CreatedSourceSystem = "system" CreatedSourceUser = "user" )
const ( VisibleToServer = "server" VisibleToApplication = "application" VisibleToServiceInstance = "service_instance" VisibleToConnector = "connector" VisibleToProviderAccount = "provider_account" VisibleToAIProvider = "ai_provider" )
const EnvSecretKey = "APPOS_SECRET_KEY" // #nosec G101 -- environment variable name, not an embedded secret
const SecretRefPrefix = "secretRef:"
SecretRefPrefix is the string prefix that marks a field value as a secret reference. Consuming modules detect this prefix and route through Resolve instead of using the value directly.
const TemplateSingleValue = "single_value"
Variables ¶
var ( ErrRevealDisabled = errors.New("reveal_disabled") ErrRevealNotFound = errors.New("not_found") ErrRevealForbidden = errors.New("forbidden") ErrRevealRevoked = errors.New("revoked") ErrRevealExpired = errors.New("expired") ErrRevealNotAllowed = errors.New("reveal_not_allowed") )
Sentinel errors returned by RevealPayload for structured route-layer mapping.
var VisibleToValues = []string{ VisibleToServer, VisibleToApplication, VisibleToServiceInstance, VisibleToConnector, VisibleToProviderAccount, VisibleToAIProvider, }
Functions ¶
func DecryptLegacyValue ¶
DecryptLegacyValue decrypts a hex-encoded AES-256-GCM value produced by the pre-Epic-19 infra/crypto package. Format: hex(nonce || ciphertext || tag).
func EnsureRuntimeKey ¶
func ExtractSecretID ¶
ExtractSecretID returns the UUID portion of a secretRef string. If v is not a valid secretRef, it returns ("", false).
func FirstStringFromPayload ¶
FirstStringFromPayload probes the payload map for a non-empty string under each of the given keys in order, returning the first match. Returns "" if none of the keys yield a non-empty string value.
func IsAllowedAccessMode ¶
func IsSecretRef ¶
IsSecretRef reports whether v is a secretRef pointer string.
func LoadKeyFromEnv ¶
func LoadKeyFromEnv() error
LoadKeyFromEnv reads the AES-256 secret key from the APPOS_SECRET_KEY environment variable (base64-encoded 32-byte value) and stores it in memory. Must be called at startup before any encrypt/decrypt operations.
func LoadTemplatesFromDefaultPath ¶
func LoadTemplatesFromDefaultPath() error
func LoadTemplatesFromFile ¶
func NormalizeAccessMode ¶
func ReadSystemSingleValue ¶
ReadSystemSingleValue reads the plaintext of a system-managed single-value secret.
func RegisterHooks ¶
func RegisterHooks(app *pocketbase.PocketBase)
func ValidatePayload ¶
ValidatePayload checks that:
- The payload contains no keys outside the template field definitions.
- Every required field is present and non-empty.
This must be called before encrypting to prevent structurally invalid payloads from being stored.
func ValidatePolicy ¶
ValidatePolicy checks the raw policy map for type/value errors. NOTE: This function mutates the input map — it normalises valid fields in-place (e.g. string integers → int, unknown access modes → default).
func ValidateRef ¶
ValidateRef checks that secretID exists and that userID is permitted to bind it as a secretRef in another module's config field.
Rules:
- Secret must exist and not be revoked.
- If scope == "user_private", only the owner (created_by == userID) may bind it.
- If scope == "global" (or empty/unset), any authenticated user may bind it.
Types ¶
type Policy ¶
type Policy struct {
RevealDisabled bool `json:"revealDisabled"`
DefaultAccessMode string `json:"defaultAccessMode"`
ClipboardClearSeconds int `json:"clipboardClearSeconds"`
// MaxAgeDays is the maximum lifetime of a secret in days.
// 0 means secrets never expire.
MaxAgeDays int `json:"maxAgeDays"`
// WarnBeforeExpiryDays controls how many days before expiry the UI shows a warning.
// 0 means no warning is shown.
WarnBeforeExpiryDays int `json:"warnBeforeExpiryDays"`
}
Policy is a value object that governs secret reveal and access behaviour for the entire platform. It is stored in sysconfig and applied at runtime.
func DefaultPolicy ¶
func DefaultPolicy() Policy
func GetPolicy ¶
GetPolicy loads the current secret policy from sysconfig. Returns DefaultPolicy() when app is nil or when no policy has been configured.
func NormalizePolicy ¶
type ResolveError ¶
ResolveError is a structured error returned by Resolve and ValidateRef. Callers receive a machine-readable SecretID alongside the human-readable Reason.
func (*ResolveError) Error ¶
func (e *ResolveError) Error() string
func (*ResolveError) Unwrap ¶
func (e *ResolveError) Unwrap() error
type ResolveResult ¶
type ResolveResult struct {
TemplateID string
Payload map[string]any
ExpiresAt string // RFC 3339 or empty
}
ResolveResult carries the decrypted payload together with expiry metadata so callers (e.g. the HTTP resolve handler) can surface expiry status.
func Resolve ¶
func Resolve(app core.App, secretID, userID string) (*ResolveResult, error)
Resolve looks up secretID from the DB, validates it is active, decrypts the payload, records last_used_at / last_used_by, emits a "secret.use" audit event, and returns a ResolveResult.
Format support:
- New format (Epic 19): `payload_encrypted` field — AES-256-GCM, base64 JSON blob.
- Legacy format (pre-Epic 19): `value` field — AES-256-GCM, hex-encoded, decrypted via DecryptLegacyValue. Result is wrapped as {"value": <plaintext>}.
The returned plaintext map MUST NOT be persisted by the caller (AC5). Resolve is synchronous and not cached in MVP.
type RevealResult ¶
RevealResult carries the decrypted payload together with record metadata so the caller (HTTP handler) can write audit entries with full context.
func RevealPayload ¶
RevealPayload performs the full reveal operation atomically:
- Checks the platform-wide reveal policy.
- Validates ownership, revocation, and access_mode.
- Decrypts the payload.
- Downgrades reveal_once → use_only inside the same transaction.
It does NOT write audit — the caller is responsible for that because audit entries may include HTTP-layer context (IP, User-Agent).
type Secret ¶
type Secret struct {
// contains filtered or unexported fields
}
Secret is the aggregate root for the Security and Secret Management domain. It wraps a PocketBase record and exposes typed domain accessors, consolidating all domain rules about what a secret IS and what is ALLOWED on it.
Callers should obtain a Secret via From(rec) and use its methods for all domain-level decisions instead of inspecting raw Record fields directly.
func FindSystemSecretByNameAndType ¶
FindSystemSecretByNameAndType loads a system-managed secret record by its canonical name and type.
func UpsertSystemPayloadSecret ¶
func UpsertSystemPayloadSecret(app core.App, secret *Secret, name, templateID string, payload map[string]any) (*Secret, error)
UpsertSystemPayloadSecret creates or updates a system-managed encrypted-payload secret.
func UpsertSystemSingleValue ¶
func UpsertSystemSingleValue(app core.App, secret *Secret, name, secretType, plaintext string) (*Secret, error)
UpsertSystemSingleValue creates or updates a system-managed single-value secret using payload_encrypted. It sets the type and clears the legacy value field in the same save as UpsertSystemPayloadSecret.
func (*Secret) AccessMode ¶
func (*Secret) CanBindByUser ¶
CanBindByUser reports whether userID may bind or consume this secret. Global secrets are available to any non-empty caller identity. user_private secrets are restricted to their creator.
func (*Secret) CanReveal ¶
CanReveal reports whether the current access_mode permits payload reveal.
func (*Secret) ExpiresAt ¶
ExpiresAt returns the expiry time of this secret, or zero if no expiry is set.
func (*Secret) IsExpiringSoon ¶
IsExpiringSoon reports whether the secret will expire within warnDays days. Returns false when warnDays is 0 or the secret has no expiry.
func (*Secret) IsOwnedBy ¶
IsOwnedBy reports whether auth is permitted to manage this secret. Superusers may manage any secret; regular users only their own.
func (*Secret) IsRevoked ¶
IsRevoked reports whether this secret has been revoked and can no longer be used.
func (*Secret) IsSystemManaged ¶
IsSystemManaged reports whether this secret was provisioned by the platform itself. System-managed secrets are read-only and cannot be updated or deleted by users.
func (*Secret) SetAccessMode ¶
SetAccessMode updates the access_mode field on the underlying record.
func (*Secret) TemplateID ¶
type Template ¶
type Template struct {
ID string `json:"id"`
Label string `json:"label"`
Description string `json:"description,omitempty"`
Fields []TemplateField `json:"fields"`
}