Help us shape the future of Go! Take the 2026 Go Developer Survey

identity

package module
v0.0.0-...-ccd85b0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 24, 2026 License: MIT Imports: 30 Imported by: 43

README

identity

The experimental identity API composes authentication v2, authorization v3, capability v2 and password v2. Consumers supplying those primitives' public values must use the matching major-version imports. Go 1.27 is required. Development source pins do not imply a tagged platform release or completed cross-module qualification; the live identity progress record lists pending work.

Experimental Go 1.27 backend libraries; no published release or complete identity-platform qualification is claimed. Start with the documentation index, reference composition and contribution guidance. HTTP features are explicitly opt-in. The original scaffold goal and threat model remain historical requirements, not evidence that implementation is absent or that security qualification passed.

The schema-v2 module inventory describes current ownership and partial delivery. Its gate flags preserve the existing development selection; unconfigured release operations are not passes or exemptions from full-platform qualification.

Capability v2 compatibility

Public proof/grant contracts use github.com/faustbrian/go-capability/v2 and the separately selected github.com/faustbrian/go-capability/postgres/identity adapter. Configure ServiceConfig.Issuer, each verifier key's Issuer and attempted authorization from trusted deployment/application policy, never a parsed token claim. Existing cap1 bytes and identity one-use/recovery histories retain their formats; verification still does not consume a proof. The root capability PostgreSQL legacy-store migration is separate from the identity adapter's coordinator-owned ledger and must not be applied to those tables.

Original password-reset preparation

The separate NewFeaturePhonePasswordResetInputCommand and generated NewFeaturePhonePasswordResetCommand retain the canonical capability, phone OTP and independent TOTP together with the original password or generated verifier. Only their exact native phone-reset descriptor is admitted. Legacy email-reset, ordinary feature and single-code accessors reject this compound variant. These are preparatory custody contracts, not a phone-recovery service or enabled route: the native owner must still bind original pre-auth/current authority, reserve separate completion risk evidence and atomically finalize every proof, password change and session revocation. This profile issues no session or remember choice.

BindPhonePasswordResetPreAuth adds the original browser cookie to either exact joint variant without replacing its password/verifier or proof/code owners. PhonePasswordResetPreAuthCookie lends it only to that closed native profile; generic feature, cookie and single-code access remain unavailable. Binding checks canonical cookie syntax, not possession or the current session-owned flow.

NewFeaturePasswordResetInputCommand borrows the primitive replacement Secret and original capability Proof before hashing. Select PasswordResetInputFeature and PasswordResetInputCustody together; no legacy or generated accessor admits this variant. Keep both owners alive through bounded preparation and destroy the password afterward. Proof aliases share upstream terminal transfer, and metadata does not establish liveness. The native coordinator must bind exact original bytes, authenticate durable issuance and current recovery authority, and screen risk/HIBP before acknowledged generation. Use NewFeaturePasswordResetCommand only for the generated verifier. This transport is not a public reset service.

Safe identifier snapshots

NewIdentifierProjection selects one identifier from a complete consistent IdentifierState under the exact expected tenant/user/record. The feature owner must first authorize the operation and obtain its primary read or committed state. Pass one trusted observation time and an explicit output selection: IdentifierValuesOmitted retains no canonical/display values; IdentifierValuesIncluded permits sensitive Values() access.

Bindings() returns copied scope, lifecycle state, independent user/authority/ identifier/verification versions and original verification time. Pending, revoked and administratively inspected blocked-user snapshots remain valid; they are not usable authentication authority. Construction neither normalizes IDs nor verifies a challenge, increments a version, proves a commit or extends freshness. Invalid scope, inconsistent non-target rows and zero values fail with redacted InvalidInputError.

Copies support concurrent reads; source/returned metadata changes do not alter them. Formatting and structured logs redact; automatic JSON/text serialization is refused. An authorized feature transport selects explicit no-store response fields instead of serializing the projection or source partition wholesale.

Native password signup custody

Before journal-owned generation, NewFeaturePasswordSignupInputCommand carries the real primitive Secret and original cookie for anonymous signup. Select PasswordSignupInputFeature and PasswordSignupInputCustody together; destroy the shared secret after bounded use. This input contains no verifier and is not ordinary execution authority or password-free recovery input. Native preparation must bind its original bytes, perform policy/HIBP admission, and generate the verifier only after the durable generation start is acknowledged.

NewFeaturePasswordSignupCommand carries the prepared encoded verifier and original pre-auth cookie together under the exact password-signup v1 profile. Use PasswordSignupFeature and PasswordSignupCustody together in trusted native adapter mapping; older feature/password/bearer accessors deny it. Construction proves transport bounds, not HIBP approval, flow possession or permission. Native orchestration must bind both inputs into the coordinator's keyed intent and consume current flow/risk authority with required effects. Never put these materials in scalar fields, journals, logs or public results. Full signup composition remains in development; applications should not build their own workflow around this transport seam.

Composable backend identity-domain ownership for Golib. This module is under development and targets Go 1.27.

NewFeaturePreAuthCommand and NewFeatureSessionCommand retain only the original submitted cookie or token for workflows such as OTP sending that have no submitted code. OneTimeCode() remains absent. Their trusted-owner borrows, canonical transport bounds, disjoint custody, redaction and primary admission obligations are the same as the code-bearing variants below. Construction and keyed command intent are not authentication or permission. Applications do not use these structural commands as an identity endpoint; the owned feature workflow must authenticate and authorize their execution.

NewFeatureOneTimeCodeSessionCommand retains the separately submitted session bearer beside the opaque OTP code, never in scalar metadata. SessionToken() borrows it for trusted native admission; pre-auth and session custody are disjoint. Canonical transport syntax is validated, not authenticated status, possession, current authority or permission. Native session owners must derive the lookup digest under the original retained key and check primary owners. Formatting/serialization and scalar reconstruction cannot disclose/recreate the bearer. Recovery receives neither submitted token nor code.

NewFeatureOneTimeCodePreAuthCommand retains the separately submitted authentication.BearerCredential alongside an opaque OTP, outside scalar feature metadata. The cookie transport must be canonical unpadded base64url encoding of 32..64 bytes. PreAuthCookie() is a trusted native-owner borrow; never log, serialize or persist its Token(). Existing code-only constructors remain unchanged. Neither constructor proves current flow state, authorization or successful OTP verification. Native owners must derive the named commitment from the actual cookie and admit the original primary flow in every applicable sealed phase. Discard secret copies on terminal outcomes; recovery uses credential-free metadata.

Privacy snapshot integrity

PrivacyExportSnapshotV1 captures the complete 23-contributor version-1 manifest and exact subject-scoped journal or transaction-staged cuts. Immutable copies retain unsigned counters, signed PostgreSQL UTC microseconds, contract/schema versions, scope digests and the retained evidence-key version. Its key owner seals or verifies domain-separated deterministic-CBOR HMAC-SHA-256 metadata; the key is never exposed by formatting or retained in snapshot projections. Record returns an owned copy for durable persistence/reconstruction.

This is integrity, not authorization or evidence of a primary read. The actual identity command must lock the identity, reject pending destructive state, capture every cut and persist the entire record atomically before making an export runnable. Publication must subsequently recheck current authority and privacy epoch. No worker, download, erasure or lifecycle completion is inferred. Those owned workflows and adapters remain implementation work. See the profile for exact tuples and adoption limits.

PrivacyExportService now admits configured human-only request proofs through the existing UnitOfWork and recovers exact intent without an expired bearer. It preserves known commit outcomes, verifies recorded evidence-key versions and discloses only redaction-approved queued job metadata. Its PostgreSQL execution mapping and actual export workflow remain unfinished; it is not yet a usable end-to-end export feature.

Security-profile construction

NewIdentitySecurityEventV1(clock.Clock) constructs the shared typed profile builder. Build(context.Context, SecurityEventInput) returns the existing audit.Record, not a new envelope, using exact 16 KiB/256-byte profile limits, secure record IDs, canonical numeric versions and explicit primitive redaction. Registered HIBP profiles distinguish padded and explicitly unpadded checking; unknown profile identifiers are rejected rather than asserted as padded. Build supports HIBP query, match detection and range rejection; unsupported action contracts reject. HIBP changes are explicitly absent. BuildDelivery supports the closed worker-transition vocabulary, recording the original command, authenticated system actor, tenant, retained provider version, current policy and adjacent intent status versions. Both profiles use protected retention. Outcome describes the local committed transition; provider evidence is separate, so provider acceptance never asserts confirmed delivery.

BuildRiskDecision constructs protected identity.risk.decide records for trusted allow, deny, throttle and step-up classifications. It supports genuine pre-auth attempts and known user subjects, preserves actor/delegation and its required impersonation grant, and emits no provider fields. Risk allow is not authentication success or permission to execute; the risk owner must still deliver the record under its required audit policy. This builder does not install that orchestration, change counters or issue one-use evidence.

Attribution must come from trusted composition, not HIBP SubjectScope or raw HTTP input. An authentication-attempt subject must be a real canonical PreAuthTransactionID; identifier digests and fabricated user IDs are invalid. Canonical nonzero base64url IDs, uppercase ULIDs, lowercase UUIDs and 128-bit hex IDs are preserved unchanged for tenant/actor/policy attribution. Syntax validation does not prove provenance or confer authority. No arbitrary attribute, description, network, breach-count or provider-payload field exists.

The builder is immutable and concurrency-safe under the borrowed clock contract. It owns no persistence, retries, goroutines or shutdown. Persist through the existing audit sink/recorder or transactional contributor; building a record is not proof that audit delivery happened. Audit v1 is retained to preserve the existing durable adapters' record identity. Other security action contracts, provider-event integration and other durable composition remain implementation work. Delivery worker records are staged by the PostgreSQL adapter in the same transaction as the transition; an audit failure rolls back that transition.

Current implemented slices are subject/pre-authentication ID contracts, structural user/status-policy validation and authorized user reads with bounded, stable-snapshot listing, versioned typed-field preparation and authorized explicit-ID creation/recovery through a storage-neutral command owner. Pure risk, claims, retention and redaction callbacks have a bounded validated policy evaluator; the authorization engine remains the upstream service. Administrative typed-field updates enforce expected-version commands and scoped recovery through the same durable owner. Self-service profile reads/updates use a distinct safe projection and exact current-human-subject scope, bounded image origins, typed fields and recovery. Suspend/ban and restore/unban use versioned commands with distinct user authority versions, exact required-consumer manifests and pending cascade admission. Account reads expose scoped permission-filtered metadata; the shared final-access check excludes the removed link, expired/disabled methods and API keys. CheckCredentialAuthority separately fences the exact credential record version and tenant/user credential epoch against one current primary observation. It denies inactive users, disabled references, wrong ownership and exclusive expiry; ordinary user aggregate versions are not security epochs. The narrow CredentialAuthorityRepository is an owned primary-read contract, not an application-provided verifier. These metadata checks do not establish proof provenance, consume a proof, refresh validity, or replace the other authority dimensions required for session issuance. Its durable adapter remains pending. Account linking and identifier changes have exact signed-intent command admission/recovery; the platform-owned durable adapters and real feature proof ceremonies remain required before these become complete public workflows. Feature adapters can structurally match common command scope to a trusted FeatureOperation with CommandBinding.ValidateFor: exact operation/schema, safe identities, positive policy version and explicit required/omitted record. The default subject policy remains user-only. A trusted descriptor can instead select FeatureSubjectAnonymousFlow: AnonymousFlowID is required and UserID must be empty. The actual verified user/API-key/service actor stays separate; neither a flow ID nor a descriptor authenticates that actor. Kernel commands reject anonymous-flow targets. Legacy user receipt JSON retains its shape. This does not authorize or register a feature, enable execution or open the existing kernel command/receipt union. Coordinator feature phases remain in development; adapters must not substitute private transactions or journals. NewFeatureCommand freezes an explicit structural feature branch: exact binding and operation plus a bounded named scalar payload (including full uint64 versions, microsecond UTC instants and copied binary metadata). It grants no authentication, schema completeness, current authority or execution readiness. Feature adapters must exhaustively map native domain commands and validate their schemas; applications do not implement that workflow. Raw credential/bearer secrets are forbidden. Opaque commands redact formatting and deny JSON; trusted returned projections are never responses, logs or evidence snapshots. Existing kernel receipts and PostgreSQL admission remain closed to this new branch. NewFeaturePasswordHashCommand freezes existing-password change intent with separate encoded-hash custody. The legacy Feature accessor rejects this variant; native owners explicitly use PasswordHashFeature for owned, hash-free metadata and PasswordHash only for bounded keyed fingerprinting, primitive parsing and driver persistence. Transport accepts 1..16384 visible ASCII bytes without normalization; it does not validate an algorithm or password policy. Never provide plaintext or log the borrowed verifier. The password owner still must supply HIBP, reauthentication/proof consumption, reuse/session policy and the complete durable workflow. Existing-password change is the only selected profile; signup/reset/rehash and mixed OTP/bearer input remain unsupported here. This adds no password engine dependency to the kernel. Discard secret-bearing command copies after bounded use; immutable Go strings cannot be fully erased.

NewFeaturePasswordResetCommand carries the prepared replacement verifier and original upstream capability.Proof in a distinct reset-complete v1 command. PasswordResetFeature exposes owned metadata and PasswordResetCustody borrows the complete original pair. Generic, change, signup and signin paths deny it. Proof ingress is 32..4096 canonical bytes; verifier transport retains the bounds above. Construction neither verifies signatures nor transfers/consumes proof. Copies share the upstream proof lifetime: transferring any alias invalidates them all, and a bounds observation cannot promise later usability. Never log or journal the pair. Native keyed fingerprinting, current account/factor/risk and HIBP policy, atomic capability/credential/session execution and recovery remain required; this command alone does not implement the durable reset workflow.

NewFeaturePasswordChangeCommand is the separate authenticated change variant: it freezes the exact hash, original session bearer and upstream authentication.ReauthenticationProof together. PasswordChangeFeature returns owned metadata; PasswordChangeCustody borrows all three original values for trusted native intent fingerprinting. Every legacy feature/hash/session accessor rejects this variant, so an older executor cannot silently omit authentication custody. Structural proof checks use the upstream contract at original issuance, not a current clock: this grants no freshness or signed provenance. Native execution must still verify the signed original/current session and consume the proof atomically with password and session effects. Existing durable executors remain closed to this variant until that explicit admission is implemented.

NewFeatureOneTimeCodeCommand additionally retains an authentication-owned opaque submission outside the scalar payload. Command.OneTimeCode borrows only that immutable opaque value; Feature remains code-free. Construction does not verify or consume a code, issue a proof or enable durable execution. The native coordinator must fingerprint the exact code with its retained key and reject unsupported one-time phases before admission. Discard command and code copies after terminal outcomes; ambiguous recovery uses code-free intent. CommandResult.Feature carries only an exact structural feature descriptor and safe primary-record before/after versions, never a bearer or native response. Every outcome retains its descriptor; only committed record mutations have one successor version and an audit ID. Mixed kernel payloads fail validation. Nil feature fields are omitted from JSON so historical kernel replay bytes remain unchanged. Validation does not prove a commit or enable feature execution; trusted durable adapters and feature-native result policy still own those claims. Remaining destructive lifecycle commands, durable repositories, complete policy composition, directory import and privacy workflows are not implemented yet.

Explicit workflow hooks

Hook supplies typed Before and After callbacks. NewHookRunner copies their registration order, scalar field classifications, enrichment permissions and registered reason codes. The complete phase shares one bounded context; callbacks must cooperate with cancellation. They run synchronously without package locks, transactions or detached goroutines. Callback-context reentry is rejected.

Before callbacks receive independent metadata copies, may reject, and may enrich only explicitly writable registered metadata. They cannot change operation, tenant, actor, correlation or original deadline. Native workflows must separately validate current authority and apply only their explicitly selected field policy. Concurrent pre-reservation attempts can call Before more than once; it must not perform durable side effects. Enabling this runner does not enable a workflow hook.

After observes a transaction that has already closed. A callback failure is a redacted observation error, not rollback: the workflow must conserve its known commit/recovery result. Callbacks stop at the first error, are not automatically retried, and provide no exactly-once external-effect guarantee. Use the existing transactional outbox for durable external notifications. The selected native workflow owns success/replay/unknown eligibility and defaults to no callbacks.

Quick start

id, err := identity.NewSubjectID()
if err != nil {
    return err
}
wire := identity.FormatSubjectID(id)
restored, err := identity.ParseSubjectID(wire)

The compiled examples demonstrate persisted-value reconstruction. See ID semantics for API, adoption, security, operations, tradeoffs and FAQ. This is not a complete login service or administrative UI. See user and lifecycle policy for structural validation and transition constraints, including their distinction from authority and writes. See authorized user queries for the read-only service and its trusted composition boundary. No PostgreSQL adapter is implemented yet. See user listing for filters, row permissions and encrypted signed snapshot cursors; a cursor is never authority. See typed additional fields for normalization, defaults, per-field write enforcement and the boundary between prepared values and a durably authorized command. See creation and recovery for the current command boundary, durable outcome semantics and the pending PostgreSQL/keyed-admission integration. See policy composition for typed fail-closed callbacks and current read/create redaction integration. See administrative updates for field policy, replay-before- version-check semantics and required durable status/version fencing. See self-service profiles for name/image, safe additional fields, current subject policy and the pending session/keyed/SQL composition. See status commands for authorization, expiry, replay, pending cleanup and authority-preserving restore behavior. See account ownership for safe reads, credential references and the final-access boundary that transactional unlink must enforce.

Development verification

Password-signin command custody

NewFeaturePasswordSigninCommand supports the internal record-free identity/password / identity.password.signin / password-signin v1 schema, with explicit user or anonymous-flow scope. It retains the existing go-password.Secret owner and the original bounded pre-auth cookie, not a successful authentication proof. Trusted native adapters must use PasswordSigninFeature and PasswordSigninCustody together and exhaustively bind the inputs to the coordinator's keyed command intent. Generic Feature and existing OTP/hash/change/bearer paths reject this variant.

The caller owns Secret.Destroy after bounded use; all copies share terminal destruction. Never persist secret-bearing commands or use them for recovery. Construction performs no password comparison, flow verification or durable write. Native signin, risk/MFA, hash-upgrade and session composition remain required; this transport alone is not a login service.

Run Go commands with a task-owned disposable GOCACHE and delete it afterwards. The current focused checks are go test -race ./..., go vet ./..., and go test -run '^$' -fuzz '^FuzzIDParsing$' -fuzztime=10s .. Comprehensive module/release gates remain pending during implementation.

License

MIT.

Documentation

Overview

Package identity owns identity-domain values and workflows. Persistence and authentication ceremonies are composed through separate adapters. Identifiers identify records or attempts; possession does not confer authority.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidCommandID identifies noncanonical command IDs without input.
	ErrInvalidCommandID = errors.New("identity: invalid command ID")
	// ErrCommandIDEntropy identifies generation failure without provider details.
	ErrCommandIDEntropy = errors.New("identity: command ID entropy unavailable")
)
View Source
var (
	// ErrInvalidSubjectID identifies malformed, noncanonical or zero subject IDs.
	// It never contains the rejected input.
	ErrInvalidSubjectID = errors.New("identity: invalid subject ID")
	// ErrSubjectIDEntropy identifies unavailable, incomplete or all-zero entropy.
	// It never includes entropy bytes or provider error details.
	ErrSubjectIDEntropy = errors.New("identity: subject ID entropy unavailable")
	// ErrInvalidPreAuthTransactionID identifies invalid attempt IDs without input.
	ErrInvalidPreAuthTransactionID = errors.New("identity: invalid pre-auth transaction ID")
	// ErrPreAuthTransactionIDEntropy identifies invalid entropy without details.
	ErrPreAuthTransactionIDEntropy = errors.New("identity: pre-auth transaction ID entropy unavailable")
)
View Source
var (
	// ErrInvalidPrivacyExportSnapshot redacts malformed or unauthenticated bindings.
	ErrInvalidPrivacyExportSnapshot = errors.New("identity: invalid privacy export snapshot")
	// ErrInvalidPrivacyExportFragment redacts mismatched fragment metadata/content.
	ErrInvalidPrivacyExportFragment = errors.New("identity: invalid privacy export fragment")
	// ErrPrivacyExportJobIDEntropy reports unavailable export ID entropy.
	ErrPrivacyExportJobIDEntropy = errors.New("identity: privacy export ID entropy unavailable")
)
View Source
var ErrCascadeIDEntropy = errors.New("identity: cascade ID entropy unavailable")

ErrCascadeIDEntropy reports cascade generation failure without provider data.

View Source
var ErrCredentialAuthority = errors.New("identity: credential authority denied")

ErrCredentialAuthority denies incomplete, stale, inactive or mismatched credential eligibility without identifying the failing field or credential.

View Source
var ErrInvalidSecurityEvent = errors.New("identity: invalid security event")

ErrInvalidSecurityEvent rejects unsafe or unsupported profile fields without disclosing input. Construction never proves audit delivery or authorization.

View Source
var ErrUserAbsent = errors.New("identity: user absent")

ErrUserAbsent is returned by a repository for enumeration-safe absence. The query service exposes the same ForbiddenError as a denied projection.

Functions

func AccountLinkIntentDigest

func AccountLinkIntentDigest(c AccountLinkChange) ([32]byte, error)

AccountLinkIntentDigest is SHA-256 over the domain identity-account-link-v1 followed by 23 length-prefixed UTF-8 fields in the order implemented below. It is an unkeyed signed-intent binding, NOT authority, a bearer, password hash or reuse tombstone. Exact mode, metadata, fences, credential and identifier choices are bound. Canonical provider policy must run before signing. No secrets are retained; invalid inputs return a zero digest and redacted error.

func BuildAccountLinkProofPayload

func BuildAccountLinkProofPayload(s AccountLinkProofSpec) (capability.Payload, error)

BuildAccountLinkProofPayload owns the canonical eight-caveat issuance shape so feature packages need not reconstruct its binding protocol. It performs no signing, authorization, provider verification or clock read. Only a configured trusted capability signer may issue it after actual proof and policy checks. Time is second-precision UTC, expiry exclusive and no more than five minutes after issuance. The returned payload/map is caller-owned; never log it or confuse a constructed payload with a signed grant.

func BuildIdentifierProofPayload

func BuildIdentifierProofPayload(s IdentifierProofSpec) (capability.Payload, error)

BuildIdentifierProofPayload owns the canonical six-caveat issuance shape. No signing, clock, I/O or proof consumption occurs. Instants have whole-second precision, UTC persisted representation, exclusive expiry and ≤5m lifetime. The returned payload is caller-owned sensitive intent; never log it.

func BuildPrivacyExportProofPayload

func BuildPrivacyExportProofPayload(s PrivacyExportProofSpec) (capability.Payload, error)

BuildPrivacyExportProofPayload prepares one-use, exact-intent issuance after trusted composition has verified the human/session and policy. Instants must have whole-second precision and an exclusive expiry at most five minutes later. No clock, signing, authorization, I/O or consumption occurs here.

func CheckCredentialAuthority

func CheckCredentialAuthority(current CredentialAuthorityRecord, expected CredentialRef, expectedAuthorityVersion uint64, now time.Time) error

CheckCredentialAuthority checks one expected credential against the current primary projection and the original user-wide credential epoch. Exact ID, tenant, user, kind, owner and record version must match. Blocked users and disabled/expired references deny even when both versions match. Ordinary user aggregate changes do not invalidate otherwise equal credential authority. Positive uint64 counters, including MaxUint64, compare without narrowing.

now is the caller's one trusted decision-clock sample. This pure check performs no lookup, mutation, clock sampling, proof validation/consumption, issuance or refresh. Success cannot replace current global/tenant/user/identifier/factor/ provider checks, verified original proof provenance or atomic finalization.

func CheckPasswordRecoveryAuthority

func CheckPasswordRecoveryAuthority(current CredentialAuthorityRecord, expected CredentialRef, expectedAuthorityVersion uint64, expectedEnabled bool, now time.Time) error

CheckPasswordRecoveryAuthority checks the exact enabled or disabled password predecessor for recovery, never authentication. It preserves all current tenant/user/status/reference/epoch/expiry checks and requires the password owner's reference. expectedEnabled must come from original trusted intent, not client policy or an assumption. Success grants neither reset permission nor a session: verified recovery, current factors/risk and atomic one-use mutation remain mandatory. No IO, renewal, metadata substitution or mutation occurs; CheckCredentialAuthority continues to reject disabled references.

func ComparePreAuthTransactionID

func ComparePreAuthTransactionID(left, right PreAuthTransactionID) (int, error)

ComparePreAuthTransactionID compares decoded bytes, returning -1, 0 or 1. Invalid operands return 0 and ErrInvalidPreAuthTransactionID.

func CompareSubjectID

func CompareSubjectID(left, right SubjectID) (int, error)

CompareSubjectID returns -1, 0 or 1 in decoded bytewise order, not encoded ASCII order. Invalid operands return 0 and ErrInvalidSubjectID.

func FormatPreAuthTransactionID

func FormatPreAuthTransactionID(id PreAuthTransactionID) string

FormatPreAuthTransactionID returns the canonical encoding or an empty string for an invalid value. No normalization of attacker-supplied text is attempted.

func FormatSubjectID

func FormatSubjectID(id SubjectID) string

FormatSubjectID returns the canonical representation or an empty string for an invalid value, including a forged conversion from a noncanonical string.

func IdentifierChangeConsumers

func IdentifierChangeConsumers() []string

IdentifierChangeConsumers returns the exact version-one change manifest. Its consumers match the removal set in LIFECYCLE_CONSUMERS.md; neither set acknowledges asynchronous completion merely because replacement committed.

func IdentifierIntentDigest

func IdentifierIntentDigest(c IdentifierChange) ([32]byte, error)

IdentifierIntentDigest returns a bounded, deterministic domain-separated signed-intent binding. It is not authority, a password hash or a safe reuse tombstone; do not publish/store it as an unhashed private-value index. The encoding is documented in docs/identifiers.md. No normalization occurs.

Example
package main

import (
	"fmt"

	identity "github.com/faustbrian/go-identity"
)

func main() {
	digest, err := identity.IdentifierIntentDigest(identity.IdentifierChange{
		TenantID: "tenant-1", UserID: "user-1", Action: identity.IdentifierAttach,
		ID: "b", Kind: identity.IdentifierEmail, CanonicalValue: "two@example.test",
		ExpectedUserVersion: 4, ExpectedAuthorityVersion: 2,
	})
	// A digest binds intent. It is not a proof, bearer or safe tombstone.
	fmt.Println(err == nil, len(digest))
}
Output:
true 32

func IdentifierRemovalConsumers

func IdentifierRemovalConsumers() []string

IdentifierRemovalConsumers returns the complete ordered version-one semantic consumer set. Missing adapters remain pending, never implicitly not-applicable. The caller owns this new slice; mutation cannot change the retained manifest.

func PrivacyExportContributorsV1

func PrivacyExportContributorsV1() []string

PrivacyExportContributorsV1 returns the exact immutable manifest's 23 ASCII- ordered semantic owners. Adapters and cache projections are not extra owners.

func PrivacyExportIntentDigest

func PrivacyExportIntentDigest(c PrivacyExportRequestCommand) ([32]byte, error)

PrivacyExportIntentDigest binds every unsigned field to a deterministic domain-separated SHA-256 digest. This is a proof caveat, not authorization, an export snapshot or a receipt. Proof metadata itself is excluded. See docs/privacy-snapshot.md for the closed CBOR profile. Invalid input is redacted.

Types

type Account

type Account struct {
	ID       AccountID
	TenantID TenantID
	UserID   UserID
	Provider string
	Issuer   string
	Subject  string
	Version  uint64
}

Account is identity-owned safe provider-link metadata, not provider tokens or a credential verifier. Its zero value is invalid. TenantID makes partition ownership explicit even when the same user/account ID exists in two tenants. Treat snapshots as immutable. Issuer/Subject may be withheld in public results; only complete authoritative records may be validated or used in commands.

func (Account) Format

func (Account) Format(state fmt.State, verb rune)

Format prevents diagnostic exposure of provider subjects or link metadata.

func (Account) Validate

func (a Account) Validate(tenant TenantID, user UserID) error

Validate checks the exact owner and bounded canonical metadata. It never authorizes access or rewrites provider subjects/issuers. The current provider issuer profiles are HTTPS and federation URNs without query, fragment or credentials. Provider registration owns protocol-specific canonicalization and must separately establish the trusted exact issuer; no URI is fetched.

type AccountAccessSnapshot

type AccountAccessSnapshot struct {
	User     User
	Accounts []Account
	Methods  []LoginMethod
}

AccountAccessSnapshot is a bounded consistent primary view of every linked account and login method for one user. It is not an authority proof. The durable owner must re-read it under its locks, including credential/factor owner eligibility, rather than trusting caller-provided counts or this view.

type AccountID

type AccountID string

AccountID is an exact case-sensitive directory ID of at most 128 UTF-8 bytes. It preserves canonical imported IDs, including ULIDs; empty is invalid. Structural parsing is not ownership, provider proof or canonical-source policy.

func ParseAccountID

func ParseAccountID(raw string) (AccountID, error)

ParseAccountID validates structure without normalizing or changing the ID.

type AccountLinkChange

type AccountLinkChange struct {
	Mode                               AccountLinkMode
	Account                            Account
	Method                             *LoginMethod
	Reason                             PolicyReason
	ExpectedUserVersion                uint64
	ExpectedAccountVersion             uint64
	ExpectedSocialLinkVersion          uint64
	ExpectedCredentialVersion          uint64
	MatchedIdentifier                  IdentifierID
	VerifiedCanonicalIdentifier        string
	ExpectedIdentifierAuthorityVersion uint64
	ExpectedIdentifierVersion          uint64
	ExpectedVerificationVersion        uint64
}

AccountLinkChange is exact, bounded intent. Account.Version must be one more than ExpectedAccountVersion, which is zero only for a never-used record ID. Nonzero history is retained after unlink; relinking requires fresh evidence and the current tombstone fence, not a stale callback or import assertion. Method optionally attaches an owner-validated provider credential; nil is a data-only link and must not change credential authority. Do not mutate input during calls. A change is neither a provider proof nor authorization.

Implicit linking requires all three identifier fences and an exact canonical value independently established from a fresh verified provider claim under registered provider semantics. Other modes must omit those fields. An administrative reason is a registered policy token, not free-form evidence.

func (AccountLinkChange) Format

func (AccountLinkChange) Format(s fmt.State, verb rune)

Format excludes provider and identifier values from diagnostics.

type AccountLinkCommand

type AccountLinkCommand struct {
	Binding              CommandBinding
	Change               AccountLinkChange
	Proof                capability.Grant
	SessionID            string
	SessionVersion       uint64
	PreAuthTransactionID PreAuthTransactionID
}

AccountLinkCommand is frozen structural intent for the sole durable owner. Proof is the canonical verified capability grant, not caller proof material. Trusted feature composition independently verifies registered provider subject, canonical verified claims and actor session/consent (or implicit pre-auth mapping) before issuance by the configured identity authority. This command and its constructor do not mint that authority. The adapter must repeat fresh time, provider enablement, policy/session/identifier and one-use checks under locks, then atomically consume proof/risk with all mutation participants.

func (AccountLinkCommand) Format

func (AccountLinkCommand) Format(s fmt.State, verb rune)

Format redacts the entire structural proof/command envelope.

type AccountLinkConfig

type AccountLinkConfig struct {
	Query                 UserQueryConfig
	ProofIssuer           string
	ProofAudience         string
	MaximumProofAge       time.Duration
	AllowAdministrative   bool
	AdministrativeReasons []PolicyReason
	AllowImplicit         bool
	ImplicitActorID       ActorID
}

AccountLinkConfig freezes exact proof authority and opt-in policy paths. MaximumProofAge is positive and at most five minutes. Administrative reasons are 1..32 unique safe registered tokens when enabled, absent when disabled. Implicit linking requires one exact trusted workload actor ID; enabling a mode or naming an actor never grants authorization or verified provider proof.

type AccountLinkMode

type AccountLinkMode uint8

AccountLinkMode is a closed policy path, never a role or authority grant.

const (
	AccountLinkExplicit AccountLinkMode = iota + 1
	AccountLinkImplicit
	AccountLinkAdministrative
)

type AccountLinkMutation

type AccountLinkMutation struct {
	Access            AccountAccessSnapshot
	Account           Account
	SocialLinkVersion uint64
	CredentialVersion uint64
	AddsCredential    bool
}

AccountLinkMutation is copied proposed next state, never a durable receipt. AddsCredential advances credential authority once, including a currently unusable provider credential. Data-only links preserve that dimension.

ApplyAccountLink computes the identity-owned pure next state. It never transfers or merges an already-owned provider subject, including same-user duplicate links. Implicit linking requires a current verified email record and all exact authority/record/verification fences; email matching alone does not establish provider evidence. Existing access methods are conserved. Inputs are not mutated and errors return no partial state.

The platform durable owner checks exact same-command replay first, then locks tenant provider-subject uniqueness (including absent keys), record/tombstone, current primary eligibility/identifiers and all authority dimensions. It must revalidate configured provider proof, current policy, fresh actor session or pre-auth binding, and consume applicable proof/risk with state, audit/events, token-vault participant and original receipt atomically. Canonical provider registration and provider ceremonies belong to their feature owners. This function neither authorizes nor proves those durable/cryptographic effects.

func (AccountLinkMutation) Format

func (AccountLinkMutation) Format(s fmt.State, verb rune)

Format redacts the proposed linked account and credential state.

type AccountLinkProofSpec

type AccountLinkProofSpec struct {
	Command   AccountLinkCommand
	Issuer    string
	Audience  string
	ID        string
	IssuedAt  time.Time
	ExpiresAt time.Time
}

AccountLinkProofSpec is bounded unsigned issuance intent, not verified evidence. Command.Proof must be zero. Its issuer/ID/time and exact actor/ provider/identifier/continuation bindings come from trusted feature ceremony composition, never caller assertions or matching profile email alone.

type AccountLinkQuery

type AccountLinkQuery struct {
	Account Account
	Mode    AccountLinkMode
}

AccountLinkQuery binds a complete primary lookup to exact candidate metadata. Repositories retain record/tombstone and subject-key authority after unlink; they never treat absence from a user's account list as tenant-wide absence.

type AccountLinkReceipt

type AccountLinkReceipt struct {
	AccountID                 AccountID
	Mode                      AccountLinkMode
	ExpectedAccountVersion    uint64
	ExpectedSocialLinkVersion uint64
	ExpectedCredentialVersion uint64
	AccountVersion            uint64
	SocialLinkVersion         uint64
	CredentialVersion         uint64
	AddsCredential            bool
}

AccountLinkReceipt is immutable local admission metadata. It is not provider network success, current authentication authority or lifecycle acknowledgement.

type AccountLinkRecoveryRequest

type AccountLinkRecoveryRequest struct {
	CommandID  CommandID
	TenantID   TenantID
	UserID     UserID
	AccountID  AccountID
	Mode       AccountLinkMode
	ActorScope authorization.DecisionContext
}

AccountLinkRecoveryRequest grants no access merely by possession of an ID. Current policy must authorize the original target/account/mode/actor scope.

type AccountLinkRepository

type AccountLinkRepository interface {
	GetAccountLinkState(context.Context, AccountLinkQuery) (AccountLinkState, error)
}

AccountLinkRepository owns bounded consistent primary reads, including exact tenant/provider/issuer/subject collision, tombstone and implicit identifier authority. It must honor concurrent bounded contexts; no provider I/O occurs.

type AccountLinkRequest

type AccountLinkRequest struct {
	CommandID            CommandID
	Change               AccountLinkChange
	Proof                capability.Proof
	SessionID            string
	SessionVersion       uint64
	PreAuthTransactionID PreAuthTransactionID
	ActorScope           authorization.DecisionContext
	CorrelationID        string
}

AccountLinkRequest is trusted core composition input, never HTTP JSON or a caller claim of verified provider email/freshness. Preserve it across retries. Proof is purpose/target/actor/command/continuation-bound signed one-use evidence issued only after the feature's actual proof ceremony and admitted policy.

type AccountLinkResult

type AccountLinkResult struct {
	CommandID     CommandID
	CommitOutcome CommitOutcome
	AccountID     AccountID
	Version       uint64
}

AccountLinkResult conserves the authoritative commit observation. Known commitment remains known when output is withheld. Unknown carries no claimed record/version success and is recovered, not silently retried under a new ID.

type AccountLinkService

type AccountLinkService struct {
	// contains filtered or unexported fields
}

AccountLinkService owns identity admission/recovery, not provider ceremonies, tokens, transactions, goroutines or a second link store. Dependencies are trusted composition capabilities and must honor bounded concurrent calls.

func NewAccountLinkService

NewAccountLinkService validates and copies configuration without external I/O.

func (s *AccountLinkService) AccountLink(ctx context.Context, r AccountLinkRequest) (out AccountLinkResult, err error)

AccountLink verifies signed intent before primary lookup and delegates exact same-command replay/CAS/atomic effects to the sole UnitOfWork. Verify never consumes the capability. Primary lookup is admission only; the durable owner repeats authoritative collision, eligibility, proof and policy checks under its shared lock/unique-index boundary before applying any state.

func (s *AccountLinkService) RecoverAccountLink(ctx context.Context, r AccountLinkRecoveryRequest) (out AccountLinkResult, err error)

RecoverAccountLink performs only scoped primary receipt recovery under current policy. It neither re-executes nor consumes proof, mutates links or calls providers.

type AccountLinkState

type AccountLinkState struct {
	Access            AccountAccessSnapshot
	Candidate         Account
	RecordVersion     uint64
	SocialLinkVersion uint64
	CredentialVersion uint64
	SubjectOwner      *Account
	Identifiers       *IdentifierState
}

AccountLinkState is one exact consistent primary lookup, not a client claim. Candidate binds absence/collision observations to the requested exact record and tenant/provider/issuer/subject key. SubjectOwner is nil only for proven absence under that key; a foreign owner's safe metadata may be retained internally but never disclosed. RecordVersion retains the candidate ID's tombstone history. SocialLinkVersion is the exact candidate connection scope; CredentialVersion is the tenant/user credential authority dimension. Identifiers is mandatory for implicit linking and shares the exact user aggregate with Access. Snapshots are immutable during a call.

func (AccountLinkState) Format

func (AccountLinkState) Format(s fmt.State, verb rune)

Format redacts collision and primary eligibility observations.

type AccountListRequest

type AccountListRequest struct {
	TenantID   TenantID
	UserID     UserID
	ActorScope authorization.DecisionContext
}

AccountListRequest is a trusted core current-human-subject read. ActorScope must come from authenticated composition, never client JSON or a role label.

type AccountListResult

type AccountListResult struct {
	Accounts   []Account
	NextCursor string
}

AccountListResult contains at most 100 permission-filtered safe records. Empty success is an explicit non-nil empty slice. NextCursor is reserved for stable pagination and remains empty in this bounded complete-read profile.

type AccountQuery

type AccountQuery struct {
	TenantID        TenantID
	UserID          UserID
	MaximumAccounts uint16
}

AccountQuery is one exact primary partition read, bounded to MaximumAccounts. The repository must not truncate: more than the bound is an unavailable projection until stable account pagination is implemented.

type AccountRepository

type AccountRepository interface {
	ListAccounts(context.Context, AccountQuery) (AccountSnapshot, error)
}

AccountRepository owns concurrent-safe, bounded consistent primary listing. Return ErrUserAbsent for missing scope. Returned slices are immutable during a call; the service privately copies output. No provider network work occurs.

type AccountService

type AccountService struct {
	// contains filtered or unexported fields
}

AccountService owns bounded account read policy, not link authority, provider tokens, sessions, goroutines or transactions. Dependencies must be concurrency-safe.

func NewAccountService

func NewAccountService(repo AccountRepository, authorize authorization.Service, redact RedactFunc, config UserQueryConfig) (*AccountService, error)

NewAccountService freezes validated query policy without I/O. Typed-nil repositories and authorization, missing callbacks and unbounded config fail.

func (*AccountService) AccountList

func (s *AccountService) AccountList(ctx context.Context, req AccountListRequest) (result AccountListResult, err error)

AccountList authorizes before primary access, then again against current user state and each owned account. Denied rows are omitted without exposing counts. Invalid or unavailable policy/projection aborts the whole result. Clock is sampled once; no caller-supplied identity metadata becomes policy evidence.

type AccountSnapshot

type AccountSnapshot struct {
	User     User
	Accounts []Account
}

AccountSnapshot is one consistent user/account primary projection, ordered by account ID. It must not mix transactions, tenant partitions or cache versions.

type AccountUnlinkChange

type AccountUnlinkChange struct {
	TenantID                  TenantID
	UserID                    UserID
	AccountID                 AccountID
	ExpectedUserVersion       uint64
	ExpectedAccountVersion    uint64
	ExpectedSocialLinkVersion uint64
	ExpectedCredentialVersion uint64
}

AccountUnlinkChange specifies exact optimistic fences for one unlink. It is structural intent, not authorization, freshness or a command receipt. The command owner authorizes and validates fresh subject/session/credential proof independently, then locks primary state and supplies it to ApplyAccountUnlink.

type AccountUnlinkCommand

type AccountUnlinkCommand struct {
	Binding        CommandBinding
	Change         AccountUnlinkChange
	Proof          capability.Grant
	SessionID      string
	SessionVersion uint64
	RevokeProvider bool
}

AccountUnlinkCommand is structural adapter input, not independently minted authority. Proof is the canonical authenticated capability grant. The trusted service verifies its configured authority first. The durable owner must check replay before CAS, revalidate time/session/credential/policy bindings under locks, reserve/finalize its single use with the mutation, repeat final-access eligibility and atomically write tombstone, audit, authority events, provider cleanup intent and original receipt. Never serialize a bearer into a receipt.

type AccountUnlinkConfig

type AccountUnlinkConfig struct {
	Query                      UserQueryConfig
	ProofIssuer, ProofAudience string
	MaximumProofAge            time.Duration
}

AccountUnlinkConfig pins a single trusted capability issuer and audience. MaximumProofAge is positive and at most fifteen minutes; no implicit defaults.

type AccountUnlinkMutation

type AccountUnlinkMutation struct {
	Access                AccountAccessSnapshot
	Removed               Account
	TombstoneVersion      uint64
	SocialLinkVersion     uint64
	CredentialVersion     uint64
	InvalidatesCredential bool
}

AccountUnlinkMutation is a privately copied proposed next state, not a durable outcome. Removed identifies the exact original link; TombstoneVersion must remain durable after removal, preventing stale callbacks from restoring it. SocialLinkVersion always advances. CredentialVersion advances exactly once only when an authentication link was removed, even if already expired. The user aggregate advances without changing the separate user-authority dimension. Provider cleanup and session revocation are not claimed complete.

func ApplyAccountUnlink(state AccountUnlinkState, change AccountUnlinkChange, now time.Time) (AccountUnlinkMutation, error)

ApplyAccountUnlink computes one all-or-nothing identity-owned transition from locked current state. It rechecks final access rather than trusting an earlier plan, fences aggregate/account/social-link/credential versions and removes all target-dependent credentials. It performs no I/O, mutates no input and owns no locks or goroutines. Do not mutate supplied slices concurrently with a call.

The platform durable command owner must check same-command replay first, establish exact authorization/freshness and authoritative eligibility under locks, then persist this state, tombstone, authority events, audit, cleanup intents and original command receipt atomically with applicable one-use proofs/risk. This function alone neither consumes proofs nor proves a commit. Errors return no partial mutation. Clock is one trusted owner sample; expiry is exclusive. Provider credentials remain with their owner until enlisted deletion/revocation is durably initiated, never discarded by this pure step.

func (AccountUnlinkMutation) Format

func (AccountUnlinkMutation) Format(s fmt.State, verb rune)

Format redacts the proposed next state and removed-link metadata.

type AccountUnlinkPlan

type AccountUnlinkPlan struct {
	Account                Account
	RemainingUsableMethods uint16
	InvalidatesCredential  bool
}

AccountUnlinkPlan is the pure final-access decision for one exact owned link. It is not an Execute command, provider proof or permission. Removing an authentication link invalidates the credential dimension even if expired.

func CheckAccountUnlink(snapshot AccountAccessSnapshot, tenant TenantID, user UserID, accountID AccountID, expectedVersion uint64, now time.Time) (AccountUnlinkPlan, error)

CheckAccountUnlink validates a complete owned primary snapshot and determines whether one versioned unlink would orphan access. It mutates nothing and grants no authority. Call after authorization and repeat under durable locks immediately before mutation with one trusted clock sample. The owner must fence user, account, social-link and applicable credential authority versions atomically with audit, cleanup intent and replay-safe command receipt.

type AccountUnlinkQuery

type AccountUnlinkQuery struct {
	TenantID  TenantID
	UserID    UserID
	AccountID AccountID
}

AccountUnlinkQuery is an exact bounded consistent primary partition read. It must include tombstone social-link authority after removal for replay.

type AccountUnlinkReceipt

type AccountUnlinkReceipt struct {
	AccountID                                                                    AccountID
	ExpectedAccountVersion, ExpectedSocialLinkVersion, ExpectedCredentialVersion uint64
	TombstoneVersion, SocialLinkVersion, CredentialVersion                       uint64
	InvalidatesCredential                                                        bool
	ProviderOutcome                                                              ProviderRevocationOutcome
}

AccountUnlinkReceipt is the immutable local unlink admission. These positive next versions and the removed account's tombstone are persisted atomically. ProviderOutcome is initial pending/not-requested, not current external state.

type AccountUnlinkRecoveryRequest

type AccountUnlinkRecoveryRequest struct {
	CommandID  CommandID
	TenantID   TenantID
	UserID     UserID
	AccountID  AccountID
	ActorScope authorization.DecisionContext
}

AccountUnlinkRecoveryRequest grants no continuation authority by ID alone. Current exact subject policy governs disclosure of the original receipt.

type AccountUnlinkRepository

type AccountUnlinkRepository interface {
	GetAccountUnlinkState(context.Context, AccountUnlinkQuery) (AccountUnlinkState, error)
}

AccountUnlinkRepository owns primary user/account/eligibility snapshots and tombstones, not provider network work. Implementations honor bounded contexts and concurrent calls. ErrUserAbsent has no public enumeration distinction.

type AccountUnlinkRequest

type AccountUnlinkRequest struct {
	CommandID      CommandID
	Change         AccountUnlinkChange
	SessionID      string
	SessionVersion uint64
	Proof          capability.Proof
	RevokeProvider bool
	ActorScope     authorization.DecisionContext
	CorrelationID  string
}

AccountUnlinkRequest is trusted core input, not a public JSON shape. Session and actor bindings come from authenticated composition, never caller claims. Retain command/change/session/proof/provider choice across retries.

type AccountUnlinkResult

type AccountUnlinkResult struct {
	CommandID       CommandID
	CommitOutcome   CommitOutcome
	Version         uint64
	ProviderOutcome ProviderRevocationOutcome
}

AccountUnlinkResult distinguishes durable observation from permitted output. Unknown/withheld output carries no version/provider success. Committed remains committed even when cancellation or redaction withholds its projection.

type AccountUnlinkService

type AccountUnlinkService struct {
	// contains filtered or unexported fields
}

AccountUnlinkService owns core admission, scoped recovery and observation. It owns no store, transaction, locks, goroutines or provider I/O. Dependencies are trusted composition capabilities and must honor concurrent bounded calls.

func NewAccountUnlinkService

func NewAccountUnlinkService(repo AccountUnlinkRepository, uow UnitOfWork, auth authorization.Service, redact RedactFunc, caps capability.Service, config AccountUnlinkConfig) (*AccountUnlinkService, error)

NewAccountUnlinkService validates dependencies/configuration without I/O.

AccountUnlink verifies exact one-use capability bindings and current primary admission, then delegates replay/CAS and atomic effects to the sole UnitOfWork. Signature validation never consumes the proof. Only same-command replay may pass older expected versions; the durable owner rejects new stale commands.

RecoverAccountUnlink performs only scoped primary recovery; no proof is consumed, account changed, new command allocated or provider call made.

type AccountUnlinkState

type AccountUnlinkState struct {
	Access            AccountAccessSnapshot
	AccountID         AccountID
	SocialLinkVersion uint64
	CredentialVersion uint64
}

AccountUnlinkState is the complete primary state at the durable unlink lock boundary. AccountID binds SocialLinkVersion to that exact connection; CredentialVersion is the tenant/user credential authority dimension, not a credential record version. All versions are positive. Access must include every current account and owner-validated login eligibility observation. This projection is not an authentication proof and must never be client input.

func (AccountUnlinkState) Format

func (AccountUnlinkState) Format(s fmt.State, verb rune)

Format redacts the entire primary account/credential projection.

type ActorID

type ActorID string

ActorID identifies the authenticated policy viewer; it is not an identity proof.

type AfterHookInput

type AfterHookInput struct {
	Context     OperationContext
	Observation HookObservation
}

AfterHookInput contains the original operation and an owned outcome copy.

func (AfterHookInput) Format

func (AfterHookInput) Format(s fmt.State, _ rune)

Format suppresses callback metadata in diagnostics.

type AssessRiskFunc

type AssessRiskFunc func(context.Context, RiskPolicyInput) (RiskPolicyDecision, error)

AssessRiskFunc is a concurrent-safe bounded pure risk callback.

type AttemptedCommitOutcome

type AttemptedCommitOutcome string

AttemptedCommitOutcome distinguishes attempt observation from durable results. Unknown conserves uncertainty and must never be interpreted as rollback.

const (
	AttemptedCommitOutcomeAttempted    AttemptedCommitOutcome = "attempted"
	AttemptedCommitOutcomeNotCommitted AttemptedCommitOutcome = "not-committed"
	AttemptedCommitOutcomeCommitted    AttemptedCommitOutcome = "committed"
	AttemptedCommitOutcomeUnknown      AttemptedCommitOutcome = "unknown"
)

type AttributeKind

type AttributeKind string

AttributeKind is a closed lossless value kind. The empty kind is invalid.

const (
	AttributeString AttributeKind = "string"
	AttributeBool   AttributeKind = "bool"
	AttributeInt64  AttributeKind = "int64"
	AttributeTime   AttributeKind = "time"
	// AttributeStringList is a list of at most 64 string scalars, not a scalar.
	AttributeStringList AttributeKind = "string-list"
)

type AttributePrepareRequest

type AttributePrepareRequest struct {
	TenantID      TenantID
	UserID        UserID
	SchemaVersion uint32
	Updates       []AttributeUpdate
	Context       authorization.DecisionContext
}

AttributePrepareRequest is trusted command composition, not a client-decoded authorization context. Supported actions are identity.user.create, identity.profile.update and identity.user.update-admin; only create fills absent defaults. SchemaVersion is exact, not a request to auto-migrate.

type AttributeProcessor

type AttributeProcessor struct {
	// contains filtered or unexported fields
}

AttributeProcessor validates, normalizes and authorizes command values. It owns no stores, transactions, goroutines or locks and is concurrent-safe if its injected dependencies are. Prepared values are NOT a proof or a durable mutation: the command owner must bind/authorize/version-fence/persist them atomically. Output permissions still require projection/redaction at reads.

func NewAttributeProcessor

func NewAttributeProcessor(schema AttributeSchema, authorize authorization.Service, normalize NormalizeAttributeFunc, config AttributeProcessorConfig) (*AttributeProcessor, error)

NewAttributeProcessor validates and deep-copies composition without callbacks or I/O. A missing/typed-nil dependency, malformed schema or default is rejected.

func (*AttributeProcessor) Prepare

func (p *AttributeProcessor) Prepare(ctx context.Context, req AttributePrepareRequest) (result []AttributeUpdate, err error)

Prepare returns ordered caller-owned values only if the whole request is valid and authorized at the configured revision. The operation and every field (including create defaults) require Allow. Self fields additionally require a user actor whose ID equals the target. Policy callbacks receive schema/field/class/sensitivity metadata, never attribute contents. Reserved metadata is replaced, not accepted from callers. No partial output survives a deny, cancellation, malformed callback output, dependency error or panic.

type AttributeProcessorConfig

type AttributeProcessorConfig struct {
	Clock                   clock.Clock
	MaximumDuration         time.Duration
	PolicyVersion           uint64
	ValidateCanonicalUserID CanonicalUserIDValidator
	ValidateValue           ValidateAttributeFunc
}

AttributeProcessorConfig binds trusted time, policy revision and canonical directory-ID validation. MaximumDuration is positive and at most one minute; no implicit defaults. The processor samples Clock once per preparation.

type AttributeScalar

type AttributeScalar struct {
	Kind   AttributeKind
	String string
	Bool   bool
	Int64  int64
	Time   time.Time
}

AttributeScalar is a tagged scalar. Only its selected arm may be populated; false, integer zero and empty string are valid present values. Time must be nonzero; preparation returns UTC. Strings are valid UTF-8 and at most 4096 bytes. There is deliberately no float conversion or arbitrary JSON payload.

type AttributeSchema

type AttributeSchema struct {
	ID      string
	Version uint32
	Fields  []FieldPolicy
}

AttributeSchema declares 1..128 unique fields and positive migration version. NewAttributeProcessor snapshots it; later caller changes have no effect.

func (AttributeSchema) Validate

func (s AttributeSchema) Validate() error

Validate checks a schema structurally without granting input, write or read authority. Scalar kinds require scalar cardinality; only StringList uses list cardinality. Normalization applies only to strings. Defaults across the schema and values across one command have a 256 KiB string payload budget.

Example
package main

import (
	"fmt"

	"github.com/faustbrian/go-identity"
)

func main() {
	schema := identity.AttributeSchema{ID: "profile", Version: 1, Fields: []identity.FieldPolicy{{
		Name: "display_name", Kind: identity.AttributeString, Cardinality: identity.CardinalityScalar,
		Normalization: identity.NormalizeNone, InputPermission: identity.FieldWrite,
		OutputPermission: identity.FieldRead, WriteAuthorization: identity.FieldWriteSelf,
		Sensitivity: identity.SensitivityPersonal, MigrationVersion: 1,
	}}}
	fmt.Println(schema.Validate())
}
Output:
<nil>

type AttributeUpdate

type AttributeUpdate struct {
	Name  string
	Value AttributeValue
}

AttributeUpdate is one named whole-value replacement, not a patch language.

type AttributeValue

type AttributeValue struct {
	Kind   AttributeKind
	Scalar AttributeScalar
	List   []AttributeScalar
}

AttributeValue is one scalar or a homogeneous bounded string list. An empty Kind is absent only for an entirely zero default; it is invalid in updates. Configure/submit values without concurrent mutation. Results own their lists.

type BeforeHookDecision

type BeforeHookDecision string

BeforeHookDecision is a closed explicit decision; zero is invalid.

const (
	BeforeHookContinue BeforeHookDecision = "continue"
	BeforeHookReject   BeforeHookDecision = "reject"
)

type BeforeHookInput

type BeforeHookInput struct {
	Context OperationContext
	Fields  []HookField
}

BeforeHookInput contains owned metadata copies and the immutable operation.

func (BeforeHookInput) Format

func (BeforeHookInput) Format(s fmt.State, _ rune)

Format suppresses callback metadata in diagnostics.

type BeforeHookResult

type BeforeHookResult struct {
	Decision    BeforeHookDecision
	Enrichments []HookEnrichment
	Reasons     []PolicyReason
}

BeforeHookResult is the ordered validated decision. Reject discards earlier enrichments; Reasons contain only registered non-secret diagnostic codes.

func (BeforeHookResult) Format

func (BeforeHookResult) Format(s fmt.State, _ rune)

Format suppresses enriched metadata in diagnostics.

type CancelledError

type CancelledError struct{}

CancelledError identifies cancellation without exposing dependency errors.

func (CancelledError) Error

func (CancelledError) Error() string

func (CancelledError) Unwrap

func (CancelledError) Unwrap() error

type CanonicalUserIDValidator

type CanonicalUserIDValidator func(context.Context, UserID) error

CanonicalUserIDValidator validates a directory ID without replacing it. Composition may accept existing ULIDs. It must be bounded, side-effect-free and concurrency-safe; errors are redacted at the service boundary.

type Cardinality

type Cardinality string

Cardinality distinguishes scalar presence from list presence. A nil list is absent; an allocated empty list is a valid explicit empty replacement.

const (
	CardinalityScalar Cardinality = "scalar"
	CardinalityList   Cardinality = "list"
)

type CascadeID

type CascadeID string

CascadeID is a canonical nonzero 128-bit random cascade identity. It is correlation, not authority. Retain it with its generation through cleanup.

func NewCascadeID

func NewCascadeID() (CascadeID, error)

NewCascadeID generates a cascade ID without exposing entropy failures.

func ParseCascadeID

func ParseCascadeID(raw string) (CascadeID, error)

ParseCascadeID accepts only the canonical unpadded 22-byte representation.

type ClaimsPolicyDecision

type ClaimsPolicyDecision struct {
	Claims        []TypedClaim
	PolicyID      string
	PolicyVersion uint64
	Reason        PolicyReason
}

ClaimsPolicyDecision is a bounded copied mapping, not authenticated authority. Reason is mandatory even for an explicitly empty mapped claim set.

type ClaimsPolicyInput

type ClaimsPolicyInput struct {
	TenantID      TenantID
	ActorID       ActorID
	OperationID   string
	PolicyVersion uint64
	Deadline      time.Time
	Issuer        string
	Subject       string
	Claims        []TypedClaim
}

ClaimsPolicyInput binds verified provider data to trusted tenant/actor and exact operation/version. Issuer is a bounded absolute HTTPS URI without credentials, query or fragment; Subject is at most 512 bytes. It proves no provider authentication by itself. Claims must be an explicit bounded slice.

type Command

type Command struct {
	// contains filtered or unexported fields
}

Command is a closed immutable storage-neutral mutation. Its zero value is invalid. It exposes no transactions, arbitrary callbacks or participants. Formatting is always redacted and JSON serialization is forbidden. Adapter mapping uses the typed accessor, never reflection or command JSON. The adapter owns versioned keyed canonical fingerprints, reservations and durable replay. Kernel variants retain exact owned types; the explicit feature branch carries bounded named typed input for exhaustive feature-adapter schema mapping. It grants no generic callback, transaction, receipt or execution authority.

func BindPhonePasswordResetPreAuth

func BindPhonePasswordResetPreAuth(original Command, cookie auth.BearerCredential) (Command, error)

BindPhonePasswordResetPreAuth retains the original browser-flow cookie with an exact joint phone-reset input or generated command. It returns a new immutable command, preserving every borrowed password/proof/code owner and leaving the original unchanged. Already-bound, legacy, mixed or zero commands and noncanonical cookies return zero Command and redacted InvalidInputError. Cookie syntax is canonical unpadded base64url for 32..64 bytes; only the native session owner may authenticate its original purpose, target and current row. Binding grants no verification or consumption authority and enables no flow.

func NewAccountLinkCommand

func NewAccountLinkCommand(s AccountLinkCommand) (Command, error)

NewAccountLinkCommand snapshots validated intent. It grants no authority, canonicalization, provider verification, freshness or database transaction.

func NewAccountUnlinkCommand

func NewAccountUnlinkCommand(s AccountUnlinkCommand) (Command, error)

NewAccountUnlinkCommand freezes validated intent. Construction does not authorize or establish freshness; only trusted composition crosses UnitOfWork.

func NewFeatureCommand

func NewFeatureCommand(spec FeatureCommandSpec) (Command, error)

NewFeatureCommand validates and freezes structural feature input, copying every field and binary value. It conveys no authentication, authorization, current state, freshness or execution readiness. The sole durable owner must match the exact registered descriptor and validate the full feature schema before computing its keyed fingerprint and admitting any phase. Current kernel coordinator execution/receipt paths do not accept this variant. Invalid input returns redacted InvalidInputError; zero is invalid. Pure, bounded construction performs no I/O/callbacks and starts no goroutines.

Example
package main

import (
	"fmt"
	"math"

	"github.com/faustbrian/go-authorization/v3"
	identity "github.com/faustbrian/go-identity"
)

func main() {
	// Trusted adapter mapping, not authentication, authorization or issuance.
	c, err := identity.NewFeatureCommand(identity.FeatureCommandSpec{
		Operation: identity.FeatureOperation{Owner: "identity/session", OperationID: "identity.session.issue", SchemaID: "session-issue", SchemaVersion: 1, RecordRequirement: identity.FeatureRecordRequired},
		Binding: identity.CommandBinding{
			CommandID: "AQEBAQEBAQEBAQEBAQEBAQ", TenantID: "tenant-a", UserID: "user-a",
			ActorID: "actor-a", ActorKind: authorization.SubjectUser,
			OperationID: "identity.session.issue", RecordID: "session-a",
			PolicyID: "session-policy", PolicyVersion: 1,
			SchemaID: "session-issue", SchemaVersion: 1, CorrelationID: "correlation-a",
		},
		Fields: []identity.FeatureField{{Name: "authority.user-version", Value: identity.FeatureValue{Kind: identity.FeatureUint64, Uint64: math.MaxUint64}}},
	})
	if err != nil {
		panic("invalid example command")
	}
	projection, _ := c.Feature() // trusted mapping only; never log the projection
	fmt.Println(projection.Fields[0].Value.Uint64)
	fmt.Println(c)
}
Output:
18446744073709551615
identity.Command(redacted)

func NewFeatureOneTimeCodeCommand

func NewFeatureOneTimeCodeCommand(spec FeatureCommandSpec, code authentication.OneTimeCode) (Command, error)

NewFeatureOneTimeCodeCommand freezes structural feature intent and one opaque submitted code for a trusted native adapter. Code bytes never enter FeatureValue or FeatureCommandSpec, and Feature returns only the code-free metadata. This carries custody, not successful verification, authorization or one-time use. The owning feature validates its exact schema, scope and code profile; the sole durable coordinator binds the secret in its keyed command fingerprint. Unsupported one-time profiles must be rejected before durable admission.

The constructor validates nonzero code through a discarded bounded digest with a public validation-only key, never a stored verifier or authority key. It accepts no request-selected digest. Existing structural bounds, defensive copying and errors are identical to NewFeatureCommand; zero code returns InvalidInputError. It performs no IO or callbacks and is safe for immutable concurrent reads. Command copies retain the secret; discard every copy on a terminal outcome and retain only code-free recovery metadata on ambiguity. Go cannot guarantee complete erasure of immutable secret storage.

func NewFeatureOneTimeCodePreAuthCommand

func NewFeatureOneTimeCodePreAuthCommand(spec FeatureCommandSpec, code authentication.OneTimeCode, cookie authentication.BearerCredential) (Command, error)

NewFeatureOneTimeCodePreAuthCommand freezes an OTP submission and its separate original pre-auth cookie in opaque command custody, never in scalar metadata. Cookie is the canonical unpadded base64url transport of 32..64 bytes. This validates syntax only: the native session/OTP owner must independently derive the named cookie commitment from the original flow, admit primary state and current policy, and consume atomically. Neither a cookie nor code is proof.

Existing one-time-code constructors remain unchanged. Invalid spec, zero code or malformed cookie returns zero Command and redacted InvalidInputError. No IO, callbacks, time samples or disclosure occur. Immutable copies are concurrency safe; discard every secret-bearing copy at terminal outcomes. Recovery uses code/cookie-free metadata; Go cannot guarantee immutable secret erasure.

func NewFeatureOneTimeCodeSessionCommand

func NewFeatureOneTimeCodeSessionCommand(spec FeatureCommandSpec, code authentication.OneTimeCode, token authentication.BearerCredential) (Command, error)

NewFeatureOneTimeCodeSessionCommand freezes an OTP submission with a separate original session token in opaque custody, never in scalar metadata. Token must be canonical unpadded base64url of 32..64 bytes. It cannot create pre-auth cookie custody. This is syntax validation, not possession proof, authenticated provenance, active status or permission; native session admission must derive the lookup digest under the original retained key and compare primary owners. Invalid scope, zero code or malformed token returns a zero Command and redacted InvalidInputError. Construction is bounded and performs no IO or callbacks. Copies are immutable and safe for concurrent reads. Never retain, persist, export or log borrowed secrets beyond bounded use. Recovery uses code/token-free metadata; Go cannot guarantee complete immutable secret erasure.

func NewFeaturePasswordChangeCommand

func NewFeaturePasswordChangeCommand(spec FeatureCommandSpec, encodedHash string, bearer authentication.BearerCredential, proof authentication.ReauthenticationProof) (Command, error)

NewFeaturePasswordChangeCommand freezes existing-password change metadata, encoded hash, original session bearer and the sole upstream reauthentication proof together. Only identity/password's record-required password-change v1 profile is admitted. Hash and bearer transport bounds are those documented by NewFeaturePasswordHashCommand and NewFeatureSessionCommand. Proof structure uses upstream authentication validation at its original issuance instant; tenant, subject and protected action must match the command and the proof purpose must be identity.password.verify. Original versions and nanoseconds are retained, not rounded or renewed. No current clock is sampled.

Construction grants no authentication or mutation authority: the native password owner must reverify the signed original proof, current source session, hash/HIBP/reuse policy and consume the proof atomically with the mutation and session effects. This transport does not verify a signature or consume a proof. Existing feature, hash-only and session accessors reject this disjoint variant; a native executor must explicitly bind all PasswordChangeCustody values into its keyed intent before admitting execution or recovery. Existing executors remain closed to it. Invalid input returns zero Command and redacted error.

Construction is bounded and pure, with no IO, callbacks or goroutines. Do not mutate inputs concurrently with construction. Copies are immutable and safe for concurrent reads; discard all secret-bearing copies after bounded use. Go cannot guarantee immutable string erasure. Never supply plaintext passwords.

func NewFeaturePasswordHashCommand

func NewFeaturePasswordHashCommand(spec FeatureCommandSpec, encodedHash string) (Command, error)

NewFeaturePasswordHashCommand freezes existing-password change metadata and an encoded hash in separate opaque custody. Hash transport is 1..16384 visible ASCII bytes, preserved exactly, never normalized. These are storage transport bounds only: the password owner must independently parse the reviewed encoding under its primitive limits, enforce HIBP/reuse/reauthentication/session policy, and bind the actual hash into the sole coordinator's keyed command intent. This function does not hash, verify, validate an algorithm or grant authority.

Only identity/password's record-required identity.password.change profile is admitted. Unsupported signup/reset/rehash profiles and mixed OTP/bearer custody are not inferred. Invalid input returns zero Command and redacted InvalidInput. The legacy Feature accessor deliberately rejects this variant, preventing older coordinators from dropping material from command fingerprints. A native owner must explicitly select PasswordHashFeature and PasswordHash together. Construction performs no IO/callbacks and starts no goroutines. Command copies retain immutable verifier material; discard all copies after bounded use. Go cannot guarantee erasure of immutable strings. Never pass a plaintext password.

func NewFeaturePasswordResetCommand

func NewFeaturePasswordResetCommand(spec FeatureCommandSpec, encodedHash string, proof capability.Proof) (Command, error)

NewFeaturePasswordResetCommand retains prepared replacement verifier material and the original upstream capability proof for a trusted native reset owner. Only identity/password's identity.password.reset-complete, password-reset-complete v1, record-required, user-targeted descriptor is supported. Hash transport is 1..16384 visible ASCII bytes, unchanged; proof transport is 32..4096 canonical bytes observed through upstream ValidateBounds. Invalid input returns a zero Command and redacted InvalidInputError without transferring the proof.

This is structural custody, not signature validation, current account/factor authority, HIBP/hash policy, issuance provenance or permission to execute. The native owner must bind both original values into its keyed intent and reserve, mutate credentials, apply session effects and finalize capability use in one recoverable transaction. Existing execution paths reject this variant.

Metadata and verifier are frozen; proof copies retain upstream shared lifetime semantics. Transfer through any alias invalidates all aliases. A successful bounds observation is not a liveness lease. No token bytes are copied, exposed, verified or transferred here. Construction performs no IO, callbacks or clock reads. Concurrent reads and upstream transfers are safe; callers must discard all secret-bearing copies after bounded use. Never pass a plaintext password.

func NewFeaturePasswordResetInputCommand

func NewFeaturePasswordResetInputCommand(spec FeatureCommandSpec, password primitive.Secret, proof capability.Proof) (Command, error)

NewFeaturePasswordResetInputCommand freezes original password-reset input before a replacement verifier exists. It admits only the exact user-targeted password-reset-complete v1 descriptor. The password is 1..1 MiB and proof transport is 32..4096 canonical bytes; workflow policy may narrow both. No bytes are copied, normalized, hashed, verified or consumed. Construction grants no issuance provenance, current recovery authority or execution.

This variant is preparation-only. The existing journal binds original borrowed password/proof custody before acknowledged, admission-gated generation. Only NewFeaturePasswordResetCommand selects the generated verifier for execution. Callers must keep both owners alive during bounded use and destroy their password afterward. Copies share terminal Secret destruction and upstream Proof transfer. Structural presence is not a liveness lease. No IO, callbacks, clocks or goroutines run; errors return zero and redacted InvalidInputError.

func NewFeaturePasswordSigninCommand

func NewFeaturePasswordSigninCommand(spec FeatureCommandSpec, password primitive.Secret, cookie authentication.BearerCredential) (Command, error)

NewFeaturePasswordSigninCommand freezes structural signin metadata with the existing primitive-owned password and original pre-auth cookie. The closed native profiles are identity/password, identity.password.signin, password-signin v1; identity/username, identity.username.signin, username-signin v1; and identity/phone, identity.phone.password-signin, phone-password-signin v1, all record-omitted with explicit user or anonymous-flow scope. This internal adapter schema is distinct from the public request schema. Cookie transport uses NewFeaturePreAuthCommand's canonical 32..64-byte base64url bounds.

Password length must be 1..1 MiB. No bytes are borrowed, copied, normalized, hashed or compared; copies retain the same Secret owner. The caller must Destroy that owner after bounded native execution, including failure. Length validation is not a liveness lease: destruction invalidates later borrowing. Construction grants no authentication, flow possession or execution authority. The native password owner must perform actual comparison, policy/current fences, keyed intent binding and same-commit effects under the coordinator.

Legacy Feature/OTP/hash/change/bearer accessors reject this disjoint variant rather than silently omitting the password from intent. Only the paired PasswordSigninFeature and PasswordSigninCustody accessors select it. Recovery must use secret-free correlation, never this command. Invalid inputs return zero Command and redacted InvalidInputError. No IO or callbacks run.

func NewFeaturePasswordSignupCommand

func NewFeaturePasswordSignupCommand(spec FeatureCommandSpec, encodedHash string, cookie authentication.BearerCredential) (Command, error)

NewFeaturePasswordSignupCommand freezes prepared encoded verifier material and the original pre-auth cookie in paired opaque custody. The closed native profiles are identity/password, identity.password.signup, password-signup v1 and identity/username, identity.username.signup, username-signup v1, record omitted, with explicit user or anonymous-flow scope. Hash transport is exactly 1..16384 visible ASCII bytes; cookie transport follows NewFeaturePreAuthCommand. Neither is normalized. Never pass plaintext here.

This is transport, not hash-policy/HIBP approval, flow possession, risk admission, field permission or authentication. The native password owner must independently validate preparation, reserve the original flow/risk, bind both custody values into keyed intent and commit all required effects under the sole coordinator. No IO, callbacks, clocks or goroutines run. Legacy accessors reject this disjoint variant rather than dropping secrets. Invalid input returns zero Command and redacted InvalidInputError. Copies share immutable custody and support concurrent reads; discard them after bounded use. Go cannot guarantee erasure of immutable verifier/bearer strings.

func NewFeaturePasswordSignupInputCommand

func NewFeaturePasswordSignupInputCommand(spec FeatureCommandSpec, password primitive.Secret, cookie authentication.BearerCredential) (Command, error)

NewFeaturePasswordSignupInputCommand freezes original anonymous signup input before a verifier exists. The closed profiles are identity/password, identity.password.signup, password-signup v1, and identity/username, identity.username.signup, username-signup v1, both record-omitted with anonymous-flow scope. Native execution remains explicitly selected. Cookie transport follows NewFeaturePreAuthCommand's canonical base64url bounds. Password length must be 1..1 MiB; registration policy may narrow these bounds.

No password bytes are copied, normalized, hashed or compared. Copies share the primitive Secret owner, which the caller must Destroy after bounded use on every path. Destruction invalidates borrowing, not safe metadata. This command is preparation input, never the generated hash/cookie execution variant and never a recovery input. Construction grants no flow possession, password-policy approval, authorization or permission to execute. The native journal must bind original custody before acknowledged generation, then restore the generated verifier through NewFeaturePasswordSignupCommand.

Only PasswordSignupInputFeature and PasswordSignupInputCustody select it. Legacy and generated-command accessors deny rather than discard the original password. Invalid input returns zero Command and redacted InvalidInputError. No IO, callbacks, clocks or goroutines run.

func NewFeaturePhonePasswordResetCommand

func NewFeaturePhonePasswordResetCommand(spec FeatureCommandSpec, encodedHash string, proof capability.Proof, phoneCode, totpCode auth.OneTimeCode) (Command, error)

NewFeaturePhonePasswordResetCommand retains the generated replacement verifier and all three original proof/code inputs for the same explicit phone profile. Verifier transport is 1..16384 visible ASCII bytes, unchanged, never plaintext. It does not screen or hash a password. Secret and authorization responsibilities, lifetimes and atomicity are otherwise identical to the input constructor. Preparation input and generated execution remain mutually exclusive; native owners must preserve every original input rather than issue substitute proof.

func NewFeaturePhonePasswordResetInputCommand

func NewFeaturePhonePasswordResetInputCommand(spec FeatureCommandSpec, password primitive.Secret, proof capability.Proof, phoneCode, totpCode auth.OneTimeCode) (Command, error)

NewFeaturePhonePasswordResetInputCommand freezes a joint original password, canonical reset proof, phone OTP and independent TOTP submission. Only the user-targeted identity/phone password-reset-complete.native-v1 descriptor is supported. This is structural custody, never proof of successful verification, carrier safety, risk admission or permission to consume any artifact.

Password bounds are 1..1 MiB; proof transport is 32..4096 canonical bytes. Codes retain upstream opaque custody and structural bounds. No password or token bytes are copied, normalized, exposed, verified or consumed. Callers keep borrowed owners alive during bounded use and destroy the password after use. Copies share terminal password destruction and proof transfer.

The native coordinator must explicitly bind all four inputs, original pre-auth/phone/factor authority and completion-only risk evidence. Replacement, OTP/TOTP replay advancement, capability/risk consumption and session revocation must commit atomically. Legacy email-reset and single-code accessors deny; construction enables no workflow or endpoint. Errors are redacted; no IO, callbacks, clocks or goroutines run. Immutable concurrent reads are safe.

func NewFeaturePreAuthCommand

func NewFeaturePreAuthCommand(spec FeatureCommandSpec, cookie authentication.BearerCredential) (Command, error)

NewFeaturePreAuthCommand freezes a feature command with only its submitted original pre-auth cookie, not an OTP submission. Cookie must be canonical unpadded base64url of 32..64 bytes. Construction validates syntax and scope, not possession, active flow, current authority or permission. The native owner must independently derive the original cookie commitment and check primary state before mutation; a cookie must never imply authentication. Invalid input returns zero Command and redacted InvalidInputError. No IO, clocks or callbacks run. Immutable copies support concurrent reads; discard secret-bearing copies after bounded use. Recovery uses secret-free metadata.

func NewFeatureRecoveryCodePreAuthCommand

func NewFeatureRecoveryCodePreAuthCommand(spec FeatureCommandSpec, code authentication.RecoveryCode, cookie authentication.BearerCredential) (Command, error)

NewFeatureRecoveryCodePreAuthCommand freezes a distinct recovery-code submission and its original canonical browser cookie for a trusted adapter. Neither secret enters scalar Feature metadata. Construction validates syntax only; the native owner must authenticate the original primary/continuation, compare the digest and burn the code with session effects in one transaction. This never coerces recovery material to OTP or grants factor assurance.

Zero code, malformed cookie or invalid bounded spec returns zero Command and redacted InvalidInputError. Validation uses a discarded public-key digest, never a stored verifier. No IO, clocks or caller callbacks run. Immutable copies share secret custody and are safe for concurrent reads; discard every copy on terminal outcomes. Durable recovery uses secret-free metadata. Go cannot guarantee complete erasure of immutable credential storage.

func NewFeatureSessionCommand

func NewFeatureSessionCommand(spec FeatureCommandSpec, token authentication.BearerCredential) (Command, error)

NewFeatureSessionCommand freezes a feature command with only its submitted original session token, not an OTP or authenticated proof. Token has the same bounded canonical transport as NewFeaturePreAuthCommand but remains a disjoint custody variant even when bytes match. The native session owner must derive the lookup digest under the original retained key and recheck current primary ownership, freshness and authority. Syntax alone confers none of these. Invalid input returns zero Command and redacted InvalidInputError. It performs no IO or callbacks. Immutable copies support concurrent reads; never persist, log or serialize borrowed bearer copies. Go cannot guarantee their erasure.

func NewIdentifierCommand

func NewIdentifierCommand(c IdentifierCommand) (Command, error)

NewIdentifierCommand validates exact signed intent and snapshots the command. It does not verify cryptography or consume proof; trusted service composition verifies configured issuer/audience and freshness before crossing UnitOfWork.

func NewPrivacyExportRequestCommand

func NewPrivacyExportRequestCommand(c PrivacyExportRequestCommand) (Command, error)

NewPrivacyExportRequestCommand validates and freezes exact signed metadata. It does not verify issuer trust, proof time, session freshness, authorization or storage. It never consumes proof. Only actual admission through the owned service and durable UnitOfWork can make an export runnable. This metadata-only constructor cannot manufacture original opaque proof custody.

func NewProfileUpdateCommand

func NewProfileUpdateCommand(spec ProfileUpdateCommand) (Command, error)

NewProfileUpdateCommand bounds and copies intent without granting authority.

Example
package main

import (
	"fmt"
	"github.com/faustbrian/go-authorization/v3"

	identity "github.com/faustbrian/go-identity"
)

func main() {
	command, err := identity.NewProfileUpdateCommand(identity.ProfileUpdateCommand{
		Binding:         identity.CommandBinding{CommandID: "AQEBAQEBAQEBAQEBAQEBAQ", TenantID: "tenant-a", UserID: "user-a", ActorID: "user-a", ActorKind: authorization.SubjectUser, OperationID: "identity.profile.update", PolicyID: "profile-policy", PolicyVersion: 1, SchemaID: "profile", SchemaVersion: 1, CorrelationID: "correlation-a"},
		ExpectedVersion: 2, Name: "example",
	})
	if err != nil {
		panic(err)
	}
	_, ok := command.UpdateProfile()
	fmt.Println(command, ok)
}
Output:
identity.Command(redacted) true

func NewUserCreationCommand

func NewUserCreationCommand(spec CreateUserCommand) (Command, error)

NewUserCreationCommand validates and snapshots a creation's structural input. It does NOT authorize, canonicalize directory IDs or validate a field schema. Trusted composition uses UserCreationService before crossing UnitOfWork.

Example
package main

import (
	"fmt"

	"github.com/faustbrian/go-authorization/v3"
	"github.com/faustbrian/go-identity"
)

func main() {
	id, err := identity.NewCommandID()
	if err != nil {
		panic(err)
	}
	command, err := identity.NewUserCreationCommand(identity.CreateUserCommand{
		Binding: identity.CommandBinding{
			CommandID: id, TenantID: "tenant-example", UserID: "user-example",
			ActorID: "operator-example", ActorKind: authorization.SubjectServiceAccount,
			OperationID: "identity.user.create", PolicyID: "directory-policy", PolicyVersion: 1,
			SchemaID: "profile", SchemaVersion: 1, CorrelationID: string(id),
		}, Status: identity.StatusActive,
	})
	if err != nil {
		panic(err)
	}
	fmt.Printf("%#v\n", command)
}
Output:
identity.Command(redacted)

func NewUserStatusCommand

func NewUserStatusCommand(spec UserStatusCommand) (Command, error)

NewUserStatusCommand freezes structural lifecycle intent; it grants no authorization and does not replace trusted service or locked owner policy. Nonzero expiry must be microsecond-exact and within PostgreSQL years 1..9999.

Example
package main

import (
	"fmt"

	"github.com/faustbrian/go-authorization/v3"
	identity "github.com/faustbrian/go-identity"
)

func main() {
	// Trusted composition authorizes and validates policy before sending this
	// immutable intent to the durable owner. Construction grants no authority.
	command, err := identity.NewUserStatusCommand(identity.UserStatusCommand{
		Binding: identity.CommandBinding{
			CommandID: "AQEBAQEBAQEBAQEBAQEBAQ", TenantID: "tenant-a",
			UserID: "01ARZ3NDEKTSV4RRFFQ69G5FAV", ActorID: "actor-a",
			ActorKind: authorization.SubjectUser, OperationID: "identity.user.suspend",
			PolicyID: "identity-policy", PolicyVersion: 1,
			SchemaID: "identity-lifecycle", SchemaVersion: 1, CorrelationID: "request-a",
		},
		ExpectedVersion: 12, ExpectedAuthorityVersion: 3, StatusPolicyVersion: 1,
		TargetStatus: identity.StatusSuspended, Reason: "abuse",
	})
	if err != nil {
		panic(err)
	}
	spec, ok := command.UserStatus()
	fmt.Println(ok, len(spec.RequiredConsumers()))
	fmt.Println(command)
}
Output:
true 12
identity.Command(redacted)

func NewUserUpdateCommand

func NewUserUpdateCommand(spec UpdateUserCommand) (Command, error)

NewUserUpdateCommand validates and privately copies an administrative update. It is not an authority issuer; use trusted UserUpdateAdminService composition.

Example
package main

import (
	"fmt"

	"github.com/faustbrian/go-authorization/v3"
	"github.com/faustbrian/go-identity"
)

func main() {
	id, err := identity.NewCommandID()
	if err != nil {
		panic(err)
	}
	command, err := identity.NewUserUpdateCommand(identity.UpdateUserCommand{
		Binding:         identity.CommandBinding{CommandID: id, TenantID: "tenant-example", UserID: "user-example", ActorID: "operator-example", ActorKind: authorization.SubjectServiceAccount, OperationID: "identity.user.update-admin", PolicyID: "directory-policy", PolicyVersion: 1, SchemaID: "profile", SchemaVersion: 1, CorrelationID: string(id)},
		ExpectedVersion: 2, Attributes: []identity.AttributeUpdate{{Name: "display_name", Value: identity.AttributeValue{Kind: identity.AttributeString, Scalar: identity.AttributeScalar{Kind: identity.AttributeString, String: "public-example"}}}},
	})
	if err != nil {
		panic(err)
	}
	// Construction validates structure, not policy or permission to execute.
	fmt.Printf("%#v\n", command)
}
Output:
identity.Command(redacted)

func (Command) ChangeIdentifier

func (c Command) ChangeIdentifier() (IdentifierCommand, bool)

ChangeIdentifier returns a private value copy for this variant only.

func (Command) CreateUser

func (c Command) CreateUser() (CreateUserCommand, bool)

CreateUser returns a private copy and false for an invalid/zero command. Returned contents are for trusted adapters, never diagnostics or snapshots.

func (Command) Feature

func (c Command) Feature() (FeatureCommandSpec, bool)

Feature returns an owned projection for trusted adapter schema mapping and false for zero, kernel, mixed or password-custody commands. Password owners must explicitly select their matching metadata/custody pair; older coordinators cannot silently omit verifier or submitted password material. Mutating returned fields or Bytes cannot change retained command intent. Copies of Command are immutable and safe for concurrent reads. The projection must never become a log, snapshot, public response or authentication decision. No callbacks or I/O run.

func (Command) Format

func (Command) Format(state fmt.State, verb rune)

Format prevents payload exposure for all fmt verbs, including %#v and %x.

func (Command) IdentifierProof

func (c Command) IdentifierProof() (capability.Proof, bool)

IdentifierProof returns optional request-memory custody of the original opaque proof attached by IdentifierService after admission. The boolean indicates presence, not validity or authorization. The transaction owner must reverify it with its configured capability service and reserve/consume atomically. Copies share the original proof's one-transfer lifetime; neither this accessor nor admission transfers or consumes it. NewIdentifierCommand constructs only metadata and does not attach custody. Never persist this proof.

func (Command) LinkAccount

func (c Command) LinkAccount() (AccountLinkCommand, bool)

LinkAccount returns an independent typed adapter copy for this variant only.

func (Command) MarshalJSON

func (Command) MarshalJSON() ([]byte, error)

MarshalJSON rejects implicit command persistence or accidental HTTP exposure.

func (Command) OneTimeCode

func (c Command) OneTimeCode() (authentication.OneTimeCode, bool)

OneTimeCode borrows opaque code custody only for trusted fingerprinting and counted primary verification. False and zero code are returned for ordinary, zero, kernel and mixed commands. It does not verify, consume or grant a proof. Returned copies remain immutable; discard them after use. No secret bytes, persisted digest or conversion accessor is exposed. No IO or state changes.

func (Command) PasswordChangeCustody

PasswordChangeCustody borrows the original hash, opaque source bearer and upstream proof together for trusted native fingerprinting and validation. Zero, mixed and other variants return empty values and false. Returned proof is a caller-owned value: assignment cannot change retained custody. Never serialize/log these values or put them in the journal or public responses. Borrowing grants no signed provenance, freshness, consumption or transaction authority. Immutable concurrent reads are safe; discard copies after use.

func (Command) PasswordChangeFeature

func (c Command) PasswordChangeFeature() (FeatureCommandSpec, bool)

PasswordChangeFeature returns owned hash/bearer/proof-free command metadata only for the authenticated password-change custody variant. Zero, mixed and every other variant return false. Mutating a projection cannot alter intent. This does not grant execution or authority; the native executor must also select and exhaustively bind PasswordChangeCustody. No IO or callbacks run.

func (Command) PasswordHash

func (c Command) PasswordHash() (string, bool)

PasswordHash explicitly borrows the original encoded verifier for bounded native fingerprinting, primitive parsing and driver persistence. Zero, ordinary, kernel, unsupported and mixed commands return empty/false. Never log, serialize, persist in the kernel journal or expose the borrowed string in a response; hash storage belongs only to the password adapter. This grants no password verification, proof, current authority or transaction ownership. Immutable concurrent reads are safe; discard borrowed strings after use.

func (Command) PasswordHashFeature

func (c Command) PasswordHashFeature() (FeatureCommandSpec, bool)

PasswordHashFeature returns owned hash-free metadata only for the explicit password-hash variant. Zero, ordinary, kernel, unsupported and mixed commands deny. Projection mutation cannot change retained intent. No IO or callback runs. This accessor does not admit execution, validate hash policy or prove authorization. Native mapping must also bind the separately borrowed hash.

func (Command) PasswordResetCustody

func (c Command) PasswordResetCustody() (string, capability.Proof, bool)

PasswordResetCustody borrows the exact replacement verifier and original upstream proof together for bounded native fingerprinting and validation. Presence is structural, not current proof validity: aliases share terminal Transfer and the native owner must fail closed when verification denies. Zero, mixed and other variants return zero values and false. Never serialize, log or persist this pair in a command journal, recovery record or response. Verifier storage belongs only to the password adapter; immutable Go strings cannot be completely erased. No IO, transfer or consumption occurs here.

func (Command) PasswordResetFeature

func (c Command) PasswordResetFeature() (FeatureCommandSpec, bool)

PasswordResetFeature returns an independently owned verifier/proof-free projection only for reset custody. Zero, mixed and other variants deny. Metadata survives proof transfer but grants no execution/recovery authority. Mutation of returned fields cannot change retained intent. No IO runs.

func (Command) PasswordResetInputCustody

func (c Command) PasswordResetInputCustody() (primitive.Secret, capability.Proof, bool)

PasswordResetInputCustody borrows the original Secret and proof together. Copies retain shared terminal owner lifetimes; recheck on every bounded use. Never serialize, log or persist this pair. Presence is not authentication or permission to hash, reserve or consume. Zero and other variants deny.

func (Command) PasswordResetInputFeature

func (c Command) PasswordResetInputFeature() (FeatureCommandSpec, bool)

PasswordResetInputFeature returns independently owned secret-free metadata for original reset preparation only. Zero, mixed and generated variants deny. Metadata survives owner destruction/transfer but grants no authority.

func (Command) PasswordSigninCustody

func (c Command) PasswordSigninCustody() (primitive.Secret, authentication.BearerCredential, bool)

PasswordSigninCustody lends the original primitive Secret and pre-auth bearer together to native fingerprinting/comparison. Zero, mixed and other variants return zero values and false. Secret copies share terminal destruction and concurrency rules; the borrower must check/use its live owner, never interpret true as successful password comparison. Never log, persist or serialize the borrowed bearer, password bytes or this command. No IO or callbacks run.

func (Command) PasswordSigninFeature

func (c Command) PasswordSigninFeature() (FeatureCommandSpec, bool)

PasswordSigninFeature returns an owned secret-free projection for the native password owner. Zero, mixed and other variants return false. Destruction of the password owner leaves metadata intact but grants no right to execute. Mutating returned fields cannot alter retained intent. No IO runs.

func (Command) PasswordSignupCustody

func (c Command) PasswordSignupCustody() (string, authentication.BearerCredential, bool)

PasswordSignupCustody borrows both original prepared hash and pre-auth bearer together for bounded native fingerprinting and owner validation. Zero, mixed and other variants return empty/false. Never serialize, log or retain these values in a kernel journal or public response. Hash storage is password-owned; recovery uses secret-free correlation, not this command. Borrowing provides no HIBP provenance, cookie possession, flow consumption or current authority.

func (Command) PasswordSignupFeature

func (c Command) PasswordSignupFeature() (FeatureCommandSpec, bool)

PasswordSignupFeature returns a separately owned secret-free metadata projection only for native signup custody. Zero, ordinary, mixed and other variants deny. Projection mutation cannot alter retained input. Success grants no execution, authentication or preparation authority. No IO runs.

func (Command) PasswordSignupInputCustody

func (c Command) PasswordSignupInputCustody() (primitive.Secret, authentication.BearerCredential, bool)

PasswordSignupInputCustody lends the original Secret and pre-auth bearer together for bounded native intent binding and preparation. Secret copies share terminal destruction and primitive concurrency rules. Success is not a liveness lease or password/flow verification: check the live owner when borrowing bytes. Never serialize, log or persist this custody. Zero, mixed and other variants return zero values and false. No IO or callbacks run.

func (Command) PasswordSignupInputFeature

func (c Command) PasswordSignupInputFeature() (FeatureCommandSpec, bool)

PasswordSignupInputFeature returns a separately owned secret-free original signup projection. Mutation cannot change retained intent. Zero, mixed and other variants deny. Metadata survives Secret destruction but grants no execution or recovery authority. No IO or callbacks run.

func (Command) PhonePasswordResetCustody

func (c Command) PhonePasswordResetCustody() (string, capability.Proof, auth.OneTimeCode, auth.OneTimeCode, bool)

PhonePasswordResetCustody borrows the replacement verifier, canonical reset proof, phone OTP and independent TOTP together, in that order. It authenticates nothing and grants no transaction authority. Proof aliases share terminal transfer; Go cannot erase immutable verifier/code strings completely. Discard all secret-bearing copies after bounded use and keep only code-free recovery.

func (Command) PhonePasswordResetFeature

func (c Command) PhonePasswordResetFeature() (FeatureCommandSpec, bool)

PhonePasswordResetFeature returns independently owned secret-free metadata only for the generated joint phone recovery command. Input/legacy/mixed/zero variants deny. Returned values are trusted adapter input, not public output.

func (Command) PhonePasswordResetInputCustody

func (c Command) PhonePasswordResetInputCustody() (primitive.Secret, capability.Proof, auth.OneTimeCode, auth.OneTimeCode, bool)

PhonePasswordResetInputCustody borrows the original password, canonical reset proof, phone OTP and independent TOTP together, in that order. Presence is structural only; recheck all owner lifetimes and native authority during use. Never log, serialize or persist this tuple. Zero/mixed/other variants deny.

func (Command) PhonePasswordResetInputFeature

func (c Command) PhonePasswordResetInputFeature() (FeatureCommandSpec, bool)

PhonePasswordResetInputFeature returns an independently owned secret-free projection only for original joint phone recovery preparation. Zero, mixed, generated and legacy commands deny. Metadata survives terminal owner use but never conveys authority or permission to generate a replacement verifier.

func (Command) PhonePasswordResetPreAuthCookie

func (c Command) PhonePasswordResetPreAuthCookie() (auth.BearerCredential, bool)

PhonePasswordResetPreAuthCookie borrows only the exact original browser cookie retained by BindPhonePasswordResetPreAuth. Input and generated joint commands retain their distinct variants; generic Feature, PreAuthCookie and OneTimeCode remain closed rather than dropping a required factor. This is not proof of possession or current flow admission. Never log, serialize or persist Token; discard every borrowed secret-bearing copy after bounded native use. Zero, unbound, legacy and mixed commands return zero/false. No IO or callbacks run.

func (Command) PreAuthCookie

func (c Command) PreAuthCookie() (authentication.BearerCredential, bool)

PreAuthCookie lends the exact submitted bearer only to trusted native owners for independently derived flow-cookie comparison. It never returns a caller digest or grants authentication/one-use authority. Code-free issuance custody and code-bearing submissions share this borrow; neither fabricates the other. Zero, ordinary, code-only and mixed commands return zero/false. Feature projections contain neither bearer nor code, and cannot reconstruct them. Discard borrowed copies after use; never log, serialize, persist or disclose Token. No IO or callbacks run.

func (Command) PrivacyExportProof

func (c Command) PrivacyExportProof() (capability.Proof, bool)

PrivacyExportProof returns request-memory custody attached only by the configured service. Presence is not authority: the durable owner must reverify and reserve/consume atomically. Copies share the original one-transfer lifetime. Neither this accessor nor service admission consumes/transfers bearer material; never persist it. The metadata constructor attaches no original proof.

func (Command) RecoveryCode

func (c Command) RecoveryCode() (authentication.RecoveryCode, bool)

RecoveryCode lends the exact opaque submitted recovery input to its trusted native fingerprint/comparison owner. It does not verify, consume or disclose a plaintext code. Zero, ordinary, OTP and mixed commands return zero/false. Copies remain immutable; discard after bounded use and never persist them.

func (Command) RequestPrivacyExport

func (c Command) RequestPrivacyExport() (PrivacyExportRequestCommand, bool)

RequestPrivacyExport returns an owned projection copy only for this variant. Contents are sensitive trusted-adapter input, never public diagnostics.

func (Command) SessionToken

func (c Command) SessionToken() (authentication.BearerCredential, bool)

SessionToken lends the exact submitted session bearer to trusted native owners for code-free issuance or code-bearing submissions without fabricating the other. It returns zero/false for zero, ordinary, code-only, pre-auth or mixed commands. A scalar projection cannot reconstruct this custody. Possession, current activity and authorization must be checked independently; this method grants none. Discard borrowed copies after bounded use, never log, persist or serialize Token. No code or bearer enters Feature projections or recovery.

func (Command) UnlinkAccount

func (c Command) UnlinkAccount() (AccountUnlinkCommand, bool)

UnlinkAccount returns an immutable adapter value copy for this variant only.

func (Command) UpdateProfile

func (c Command) UpdateProfile() (ProfileUpdateCommand, bool)

UpdateProfile returns a private adapter copy, never another command variant.

func (Command) UpdateUser

func (c Command) UpdateUser() (UpdateUserCommand, bool)

UpdateUser returns a private copy, or false for another/invalid command kind.

func (Command) UserStatus

func (c Command) UserStatus() (UserStatusCommand, bool)

UserStatus returns a value copy, or false for another/invalid command variant.

type CommandBinding

type CommandBinding struct {
	CommandID CommandID
	TenantID  TenantID
	UserID    UserID
	// AnonymousFlowID is used only by an explicitly anonymous-targeted feature
	// schema, mutually exclusive with UserID. It is a canonical flow identity,
	// not an authenticated actor, user, command ID or continuation credential.
	// Kernel commands forbid it; durable adapters must explicitly support it.
	AnonymousFlowID string `json:",omitempty"`
	ActorID         ActorID
	ActorKind       authorization.SubjectKind
	OperationID     string
	PolicyID        string
	PolicyVersion   uint64
	SchemaID        string
	SchemaVersion   uint32
	CorrelationID   string
	// RecordID is the exact account/identifier target for kernel record commands,
	// including every noncommitted receipt; other kernel operations omit it.
	// Feature bindings follow their FeatureOperation.RecordRequirement instead.
	RecordID string
	// LinkMode is required only for account-link commands, for every outcome.
	LinkMode AccountLinkMode
}

CommandBinding is an immutable logical command scope, not an authority proof. Positive policy/schema versions and canonical safe IDs are mandatory. Actor kind is the exact upstream kind, not a caller role or administrator flag. CorrelationID is safe correlation only; it grants no continuation authority.

func (CommandBinding) ValidateFor

func (b CommandBinding) ValidateFor(d FeatureOperation) error

ValidateFor checks every common scope field against one exact feature schema. It preserves canonical command identity, safe directory IDs, actual upstream actor kinds and positive policy versions. Required targets cannot be omitted; record-free operations cannot carry one. LinkMode must be zero. Descriptor ownership and policy must come from trusted composition, not this input. This is structural validation, not authentication, authorization, current authority, registration, a fingerprint or a transaction. Existing kernel Kernel Command constructors and kernel receipts remain separate closed variants. Feature receipts require their explicit Feature branch. Zero is invalid; invalid input returns redacted InvalidInputError. No I/O or callbacks run.

Example
package main

import (
	"fmt"

	"github.com/faustbrian/go-authorization/v3"
	identity "github.com/faustbrian/go-identity"
)

func main() {
	// Compile-time composition descriptor, not an operation supplied by a client.
	operation := identity.FeatureOperation{
		Owner: "identity/session", OperationID: "identity.session.issue",
		SchemaID: "session-issue", SchemaVersion: 1,
		RecordRequirement: identity.FeatureRecordRequired,
	}
	binding := identity.CommandBinding{
		CommandID: "AQEBAQEBAQEBAQEBAQEBAQ", TenantID: "tenant-a", UserID: "user-a",
		ActorID: "actor-a", ActorKind: authorization.SubjectUser,
		OperationID: "identity.session.issue", RecordID: "session-a",
		PolicyID: "session-policy", PolicyVersion: 1,
		SchemaID: "session-issue", SchemaVersion: 1, CorrelationID: "correlation-a",
	}
	fmt.Println(binding.ValidateFor(operation) == nil)
	binding.SchemaVersion++
	fmt.Println(binding.ValidateFor(operation) != nil)
}
Output:
true
true

type CommandID

type CommandID string

CommandID is a canonical nonzero random 128-bit command identity. It is not derived from user data; preserve it across retries, including unknown outcomes. Possession grants no identity or database authority. Empty is invalid.

func NewCommandID

func NewCommandID() (CommandID, error)

NewCommandID reads exactly 16 crypto/rand bytes and rejects all-zero entropy.

func ParseCommandID

func ParseCommandID(raw string) (CommandID, error)

ParseCommandID reconstructs only the canonical 22-byte unpadded base64url form.

type CommandResult

type CommandResult struct {
	Binding      CommandBinding
	Outcome      CommitOutcome
	User         User
	AuditEventID string
	RetryAfter   time.Duration
	// ExpectedVersion is present only on a committed update receipt; the
	// immutable result must be exactly one greater. Creation keeps it zero.
	ExpectedVersion uint64
	// Profile exists only for a committed self-service profile command. Adapters
	// retain immutable copies; callers must never mutate a receipt concurrently.
	Profile *UserProfile
	// Revocation is present only on a committed status admission. It records
	// pending cleanup, never fabricated asynchronous completion.
	Revocation *RevocationOutcome
	// Unlink is present only for a committed account unlink, never external
	// provider completion. Treat retained receipts as immutable snapshots.
	Unlink *AccountUnlinkReceipt
	// Link is present only for committed identity-owned account link admission.
	Link *AccountLinkReceipt
	// Identifier is present only on a committed identifier mutation.
	Identifier *IdentifierReceipt
	// Feature is the exclusive structural feature receipt branch. Nil is omitted
	// from JSON to preserve every historical kernel receipt byte. It never carries
	// credentials or feature-native response payloads. Treat it as immutable.
	Feature *FeatureReceipt `json:",omitempty"`
	// PrivacyExport is exclusive committed queued snapshot admission metadata.
	// Nil omission preserves historical receipt bytes. It is not a ready export
	// or current authority; verify retained intent and the recorded evidence key.
	PrivacyExport *PrivacyExportRequestReceipt `json:",omitempty"`
	// Cascade is the committed feature owner's pending lifecycle admission.
	// Nil omission preserves historical receipts. It is not consumer completion.
	Cascade *LifecycleCascadeAdmission `json:",omitempty"`
}

CommandResult is the adapter's authoritative redacted receipt, not current user authority. A committed creation returns its immutable initial version-1 user and stable audit event ID. Other outcomes carry neither user nor audit ID. InProgress carries a positive retry delay up to one minute; all others zero.

func (CommandResult) Validate

func (r CommandResult) Validate() error

Validate checks the closed receipt shape, immutable operation binding, version relationships and outcome-specific payload without authorization or current-state I/O. It returns a redacted InvalidInputError for invalid values. The zero value is invalid. Callers must not mutate a receipt concurrently.

func (CommandResult) ValidatePrivacyExportRequest

ValidatePrivacyExportRequest checks an exact receipt against retained request intent and, for committed admission, verifies the complete snapshot with its recorded evidence key. It returns an immutable copied snapshot only on a structurally committed queued admission. Known noncommitted outcomes return zero snapshot and nil error without requiring an unused evidence key. Invalid values return redacted InvalidInputError. The request need not carry a live Grant: recovery must match the original intent even after proof expiry. This does not prove a commit, authorization, fresh session, primary reads or captured cuts; the platform-owned service/adapter must establish those facts. No IO, proof consumption or job transition occurs. Do not mutate arguments concurrently. The returned snapshot cannot be changed through receipt aliases.

type CommitOutcome

type CommitOutcome uint8

CommitOutcome is the sole closed durable-command observation. Cancellation, panic, transport failure or a lease timeout is not evidence of rollback.

const (
	CommitOutcomeInvalid CommitOutcome = iota
	CommitOutcomeNotCommitted
	CommitOutcomeCommitted
	CommitOutcomeUnknown
	CommitOutcomeConflict
	CommitOutcomeInProgress
)

type ConflictError

type ConflictError struct{ CommandID CommandID }

ConflictError is a stable non-enumerating command, version or uniqueness denial.

func (ConflictError) Error

func (ConflictError) Error() string

type CreateUserCommand

type CreateUserCommand struct {
	Binding    CommandBinding
	Status     Status
	Attributes []AttributeUpdate
}

CreateUserCommand contains one prepared initial creation. Initial status is active; other lifecycle states require their own versioned lifecycle command. Attributes are not authority and must never contain credentials. Configure or construct values without concurrent mutation; Command freezes a deep copy.

type CredentialAuthorityQuery

type CredentialAuthorityQuery struct {
	TenantID     TenantID
	UserID       UserID
	CredentialID CredentialID
}

CredentialAuthorityQuery selects exactly one credential reference under its tenant and user. It contains no secret or proof and grants no lookup access.

func (CredentialAuthorityQuery) Validate

func (q CredentialAuthorityQuery) Validate() error

Validate checks exact bounded IDs without normalization or ownership claims.

type CredentialAuthorityRecord

type CredentialAuthorityRecord struct {
	User             User
	AuthorityVersion uint64
	Credential       CredentialRef
	Enabled          bool
	ExpiresAt        time.Time
}

CredentialAuthorityRecord is one consistent primary observation. User.Version is aggregate CAS; AuthorityVersion is the separate tenant/user credential epoch; Credential.Version belongs to the exact credential record. No two of these counters substitute for one another. Enabled describes only the owned reference's eligibility, not secret verification, factor/provider eligibility or authorization. ExpiresAt zero is indefinite; nonzero expiry is exclusive and must be a UTC-representable microsecond instant in years 1..9999.

The record is data, not an opaque proof or validity lease. Its construction, copying and checking cannot establish read provenance or renew freshness. Treat it as immutable during a call; it contains no verifier or bearer.

func (CredentialAuthorityRecord) Format

func (CredentialAuthorityRecord) Format(s fmt.State, _ rune)

Format keeps scope and credential metadata out of ordinary diagnostics.

type CredentialAuthorityRepository

type CredentialAuthorityRepository interface {
	GetCredentialAuthority(context.Context, CredentialAuthorityQuery) (CredentialAuthorityRecord, error)
}

CredentialAuthorityRepository is the platform-owned, concurrency-safe primary port. Reads honor a bounded context and return the user, credential epoch and reference from one consistent snapshot. Missing exact user or reference is ErrUserAbsent; corrupt/unavailable storage is redacted Unavailable. No partial positive result, fallback cache, default version or caller-supplied store may establish authority. Mutations and proof consumption belong to their owned command; this interface exposes no transaction or verification material.

type CredentialID

type CredentialID string

CredentialID names a non-secret credential reference. Empty is invalid and structural bounds are the same as directory IDs; it is never a bearer.

type CredentialKind

type CredentialKind uint8

CredentialKind is the closed credential namespace, not a login permission.

const (
	CredentialPassword CredentialKind = iota + 1
	CredentialPasskey
	CredentialProvider
	CredentialAPIKey
)

type CredentialRef

type CredentialRef struct {
	ID       CredentialID
	TenantID TenantID
	UserID   UserID
	Kind     CredentialKind
	Owner    string
	Version  uint64
}

CredentialRef is a versioned non-secret pointer to one owning feature. It contains no verifier, bearer, factor material or provider payload. Ownership and authoritative usability are separate from this structural reference.

func (CredentialRef) Format

func (CredentialRef) Format(state fmt.State, verb rune)

Format keeps references out of diagnostic snapshots despite carrying no secret.

func (CredentialRef) Validate

func (c CredentialRef) Validate(tenant TenantID, user UserID) error

Validate checks a reference's exact user and closed kind/owner pairing. Password/API-key owners are their identity features, passkey owners may be webauthn or passkey, and federation/provider owners may be sso or identity/oauth. Neither its tenant/user fields nor ID grants authority.

type Cursor

type Cursor struct {
	// contains filtered or unexported fields
}

Cursor is an immutable storage-neutral snapshot position, not authority. Its zero value starts a new snapshot. Only trusted repositories construct continuations; wire cursors are authenticated and decoded by the service.

func NewCursor

func NewCursor(snapshot SubjectID, position uint64) (Cursor, error)

NewCursor constructs a repository continuation for a canonical random snapshot ID and a positive position no greater than MaxInt64. Adapters must never substitute a user's ID for the snapshot ID or recreate expired snapshots.

Example
package main

import (
	"fmt"

	"github.com/faustbrian/go-identity"
)

func main() {
	// A trusted repository records the scanned position in its retained snapshot.
	// The service authenticates/encrypts this value for wire continuation; it is
	// not a tenant permission, user identity or credential.
	cursor, err := identity.NewCursor("AQEBAQEBAQEBAQEBAQEBAQ", 10)
	if err != nil {
		panic(err)
	}
	fmt.Println(cursor.Position())
}
Output:
10

func (Cursor) Position

func (c Cursor) Position() uint64

Position returns the number of snapshot rows already scanned.

func (Cursor) SnapshotID

func (c Cursor) SnapshotID() SubjectID

SnapshotID returns the opaque repository snapshot identifier, not a user ID.

type DeadlineExceededError

type DeadlineExceededError struct{}

DeadlineExceededError identifies expiration of the bounded read context.

func (DeadlineExceededError) Error

func (DeadlineExceededError) Error() string

func (DeadlineExceededError) Unwrap

func (DeadlineExceededError) Unwrap() error

type DecideRetentionFunc

DecideRetentionFunc is a concurrent-safe bounded pure retention callback.

type DeliverySecurityEventInput

type DeliverySecurityEventInput struct {
	Transition                     DeliverySecurityTransition
	OccurredAt                     time.Time
	TenantID                       TenantID
	IntentID                       string
	CommandID                      CommandID
	WorkerActorID, CorrelationID   string
	PolicyID                       string
	PolicyVersion, ProviderVersion uint64
	BeforeVersion, AfterVersion    uint64
}

DeliverySecurityEventInput is trusted owned-adapter attribution. IDs are stable internal tokens, not usernames, addresses or credentials. IntentID is the exact nonzero 64-byte lowercase hexadecimal effect ID. WorkerActorID is the authenticated service account, not a lease name or the message owner. Versions are the database-owned status versions before/after this transition. CommandID and causation preserve the original owning command. No fields carry recipient, rendering, provider reference, raw receipt, or arbitrary claims.

type DeliverySecurityTransition

type DeliverySecurityTransition uint8

DeliverySecurityTransition classifies committed local delivery bookkeeping, not a provider invocation or an unknown database commit. Zero is invalid.

const (
	DeliverySecurityAttempt       DeliverySecurityTransition = iota + 1 // Committed invocation lease, before provider IO.
	DeliverySecurityAccepted                                            // Provider acceptance, not delivery.
	DeliverySecurityConfirmed                                           // Authoritative delivery success.
	DeliverySecurityRejected                                            // Permanent rejection or bounce.
	DeliverySecurityRetry                                               // Authoritative transient no-success evidence.
	DeliverySecurityUnknown                                             // Observed ambiguous provider outcome.
	DeliverySecurityCancelled                                           // Local stop, not remote confirmation.
	DeliverySecurityExpired                                             // Unsent work expired and custody erased.
	DeliverySecurityExhausted                                           // Retry policy exhausted, not provider rejection.
	DeliverySecurityReleased                                            // Lease released with authoritative no-invocation evidence.
	DeliverySecurityPayloadErased                                       // Expired custody erased without inventing an outcome.
	DeliverySecurityLeaseLost                                           // Lease expired without an observed provider result.
)

type FeatureCommandSpec

type FeatureCommandSpec struct {
	Binding   CommandBinding
	Operation FeatureOperation
	Fields    []FeatureField
}

FeatureCommandSpec is a structural, storage-neutral trusted mapping input. Binding must match Operation exactly. Fields contains 0..128 distinct named scalars and at most 256 KiB counted as each name's byte length, one kind byte and value bytes (integers/time count eight, booleans one). There are no floats, executable callbacks, raw JSON/CBOR, transaction handles or arbitrary values. This public projection is not a secret-custody boundary: never log/serialize it or use it for HTTP responses. The returned opaque Command supplies the diagnostic boundary. Do not mutate inputs concurrently with construction. Feature services still own their native typed commands. Trusted adapters alone map these to this shared vocabulary; applications need no workflow glue.

type FeatureField

type FeatureField struct {
	Name  string
	Value FeatureValue
}

FeatureField is one named typed input, not an extension hook or arbitrary serialized blob. Name is at most 128 lowercase ASCII bytes in dot-separated segments with the FeatureOperation.OperationID syntax. Duplicate names deny. Named field order has no meaning; owners explicitly encode sequence indexes in field names when their schema needs ordered inputs. All behavior-affecting input, including expected versions and options, must be mapped exhaustively.

type FeatureKind

type FeatureKind uint8

FeatureKind selects exactly one named command scalar. Zero/unknown kinds are invalid; inactive members must be exactly zero (including nil Bytes).

const (
	// FeatureText selects bounded UTF-8 text without implicit normalization.
	FeatureText FeatureKind = iota + 1
	// FeatureBytes selects bounded binary metadata, never a raw bearer/secret.
	FeatureBytes
	// FeatureUint64 preserves the complete unsigned authority-version range.
	FeatureUint64
	// FeatureInt64 selects a signed integer (including duration microseconds).
	FeatureInt64
	// FeatureBool selects an explicit boolean, including false.
	FeatureBool
	// FeatureTime selects a nonzero microsecond-exact year 1..9999 UTC instant.
	FeatureTime
)

type FeatureOperation

type FeatureOperation struct {
	Owner             string
	OperationID       string
	SchemaID          string
	SchemaVersion     uint32
	RecordRequirement FeatureRecordRequirement
	// SubjectRequirement is frozen schema policy, not selected by request data.
	// Zero retains the original user-only contract and serialized shape.
	SubjectRequirement FeatureSubjectRequirement `json:",omitempty"`
}

FeatureOperation identifies one storage-neutral feature-owned operation. Trusted composition supplies a compile-time descriptor, never request data. Owner is a canonical lowercase slash-separated module label; OperationID is a lowercase dot-separated operation name (each segment starts with a letter, ends with a letter/digit and otherwise allows letters, digits and hyphens). Both are bounded to 128 bytes. SchemaID follows existing safe directory-ID rules; SchemaVersion is positive. The record requirement is explicit. Descriptors are scalar values: freeze them at composition and do not mutate one concurrently with validation. Validation performs no I/O, registration, authorization or command execution. Kernel-owned operations are excluded.

func (FeatureOperation) Validate

func (d FeatureOperation) Validate() error

Validate checks structural descriptor identity, not ownership authenticity. The owning feature adapter must validate its full typed command and selected policy; the coordinator must match the exact immutable registered descriptor before admission. Names alone never enable an execution phase. Invalid input returns a redacted InvalidInputError; zero is invalid.

type FeatureReceipt

type FeatureReceipt struct {
	Operation     FeatureOperation
	BeforeVersion uint64
	AfterVersion  uint64
}

FeatureReceipt is a storage-neutral safe primary-record receipt, never a feature-native response or authentication proof. Operation retains the exact trusted descriptor on every outcome. A committed record-target operation has AfterVersion == BeforeVersion+1 without overflow; creation starts at 0 -> 1. Record-free operations and every noncommitted outcome keep both versions zero. The target ID is CommandResult.Binding.RecordID, not an alternate locator. Feature owners validate their native result and complete command schema; this shared vocabulary carries no arbitrary payload, bearer or secret. Public fields permit trusted durable reconstruction; zero is invalid through CommandResult.Validate. Callers must not mutate a receipt concurrently. Structural validation does not confirm a commit, grant current authority or enable coordinator execution; only the owning durable outcome establishes it.

Example
package main

import (
	"fmt"

	"github.com/faustbrian/go-authorization/v3"
	identity "github.com/faustbrian/go-identity"
)

func main() {
	// A structural trusted-adapter projection, not issuance or commit evidence.
	r := identity.CommandResult{
		Binding: identity.CommandBinding{CommandID: "AQEBAQEBAQEBAQEBAQEBAQ", TenantID: "tenant-a", UserID: "user-a", ActorID: "actor-a", ActorKind: authorization.SubjectUser, OperationID: "identity.session.issue", RecordID: "session-a", PolicyID: "session-policy", PolicyVersion: 1, SchemaID: "session-issue", SchemaVersion: 1, CorrelationID: "correlation-a"},
		Outcome: identity.CommitOutcomeCommitted, AuditEventID: "audit-a",
		Feature: &identity.FeatureReceipt{Operation: identity.FeatureOperation{Owner: "identity/session", OperationID: "identity.session.issue", SchemaID: "session-issue", SchemaVersion: 1, RecordRequirement: identity.FeatureRecordRequired}, AfterVersion: 1},
	}
	if r.Validate() != nil {
		panic("invalid example receipt")
	}
	fmt.Println(r.Feature.BeforeVersion, r.Feature.AfterVersion)
}
Output:
0 1

type FeatureRecordRequirement

type FeatureRecordRequirement uint8

FeatureRecordRequirement fixes whether an operation has one exact record target. It is a schema decision, not inferred from submitted input. Zero and unknown values are invalid; feature operations never borrow account LinkMode.

const (
	// FeatureRecordOmitted requires an empty CommandBinding.RecordID.
	FeatureRecordOmitted FeatureRecordRequirement = iota + 1
	// FeatureRecordRequired requires a valid exact CommandBinding.RecordID.
	FeatureRecordRequired
)

type FeatureSubjectRequirement

type FeatureSubjectRequirement uint8

FeatureSubjectRequirement fixes an operation's target identity namespace at trusted composition. The default preserves existing user-targeted schemas. Anonymous-flow targeting is explicit and never substitutes a flow for UserID. This does not authenticate a flow or grant execution/recovery authority.

const (
	// FeatureSubjectUser requires UserID and forbids AnonymousFlowID.
	FeatureSubjectUser FeatureSubjectRequirement = iota
	// FeatureSubjectAnonymousFlow requires AnonymousFlowID and an empty UserID.
	// The actor remains a separately verified user, API key or service account.
	FeatureSubjectAnonymousFlow
)

type FeatureValue

type FeatureValue struct {
	Kind   FeatureKind
	Text   string
	Bytes  []byte
	Uint64 uint64
	Int64  int64
	Bool   bool
	Time   time.Time
}

FeatureValue is a closed scalar union for trusted feature command mapping. Kind is required even for empty text/bytes, false or zero integers. Text/Bytes are at most 8192 bytes; text must be valid UTF-8. Time is normalized to UTC without changing its instant. Values are command input, not authentication or an authority proof. Never place raw passwords, session/API-key bearers, private keys or provider tokens here. Feature owners define and validate exact field semantics, including confidentiality and cryptographic evidence provenance.

type FieldAuthorization

type FieldAuthorization string

FieldAuthorization is a required write class passed to the upstream policy service. Administrator/system strings never grant a caller either role.

const (
	FieldWriteSelf          FieldAuthorization = "self"
	FieldWriteAdministrator FieldAuthorization = "administrator"
	FieldWriteSystem        FieldAuthorization = "system"
)

type FieldName

type FieldName struct{ Value string }

FieldName is a canonical schema field name, never an attribute value.

type FieldPermission

type FieldPermission string

FieldPermission is an explicit input or output permission, not authorization.

const (
	FieldDenied    FieldPermission = "denied"
	FieldRead      FieldPermission = "read"
	FieldWrite     FieldPermission = "write"
	FieldReadWrite FieldPermission = "read-write"
)

type FieldPolicy

type FieldPolicy struct {
	Name               string
	Kind               AttributeKind
	Cardinality        Cardinality
	Default            AttributeValue
	Normalization      NormalizationPolicy
	InputPermission    FieldPermission
	OutputPermission   FieldPermission
	WriteAuthorization FieldAuthorization
	Sensitivity        Sensitivity
	Indexed            bool
	MigrationVersion   uint32
}

FieldPolicy defines one immutable additional field. Names use lower ASCII letters, digits and underscores, start with a letter and are 1..128 bytes. Core id/tenant_id/status/version names are reserved. Defaults must match kind and cardinality. Indexed=false is valid. MigrationVersion must equal the schema Version; incompatible versions require an explicit new schema.

type FinalAccessError

type FinalAccessError struct{}

FinalAccessError means unlink would leave no independent human login method. Public workflows must authorize before running this enumeration-sensitive check.

func (FinalAccessError) Error

func (FinalAccessError) Error() string

type ForbiddenError

type ForbiddenError struct{}

ForbiddenError is a redacted policy denial or enumeration-safe absence.

func (ForbiddenError) Error

func (ForbiddenError) Error() string

type GetUserQuery

type GetUserQuery struct {
	TenantID TenantID
	UserID   UserID
	Fields   []FieldName
}

GetUserQuery is an exact tenant/user lookup with a bounded safe projection. Repository implementations must not return another subject or tenant.

type Hook

Hook supplies cooperative, bounded, concurrency-safe application callbacks. Before runs outside transactions and must not perform durable side effects: concurrent attempts may call it before the sole reservation selects a winner. After runs after transaction closure; its failure cannot undo committed work. Neither phase receives a store, transaction, proof or continuation authority. Use the durable outbox, not these callbacks, for retryable external effects.

type HookEnrichment

type HookEnrichment struct{ Fields []HookField }

HookEnrichment replaces or adds registered writable metadata only. It never changes OperationContext or grants permission to modify protected authority.

type HookField

type HookField struct {
	Name        string
	Value       AttributeScalar
	Sensitivity Sensitivity
}

HookField is one registered bounded scalar. Credentials are forbidden even when marked secret. Diagnostic formatting deliberately hides every value.

func (HookField) Format

func (HookField) Format(s fmt.State, _ rune)

Format suppresses callback metadata in diagnostics, including sensitive IDs.

type HookFieldRegistration

type HookFieldRegistration struct {
	Name        string
	Kind        AttributeKind
	Sensitivity Sensitivity
	Enrichment  bool
}

HookFieldRegistration fixes one scalar's kind/classification and whether before hooks may enrich it. Core reserved fields cannot be writable. Domain adapters must register only metadata safe for their selected workflow.

type HookObservation

type HookObservation struct {
	Outcome AttemptedCommitOutcome
	Notes   []PolicyReason
}

HookObservation is safe outcome metadata after transaction closure. Notes are registered codes, never user values, credentials or replay payloads. Notes must be non-nil; an explicit empty collection observes no extra notes.

type HookRunner

type HookRunner struct {
	// contains filtered or unexported fields
}

HookRunner runs1..64 callbacks in registration order, stopping at the first rejection/failure. One shared bounded budget covers the complete phase. Callback-context reentry into any HookRunner is rejected, including After. Dependencies are borrowed and must be concurrency-safe; the runner owns no lock, goroutine or resource lifetime. Zero/nil runners are invalid.

func NewHookRunner

func NewHookRunner(hooks []Hook, config HookRunnerConfig) (*HookRunner, error)

NewHookRunner validates and copies ordered callbacks and field/reason policy. No callback, IO, transaction or provider runs during construction.

func (*HookRunner) After

func (r *HookRunner) After(ctx context.Context, in AfterHookInput) error

After observes an already closed transaction and stops on the first error. Errors/panics are redacted, not translated to rollback. The workflow must conserve its original known result even if this returns an error. There is no automatic retry, exactly-once external-effect claim or replay observation.

func (*HookRunner) Before

Before invokes ordered hooks with independent metadata copies. Enrichments must match registration and may write each field at most once across a phase; at most64 total fields/enrichments and32 registered reasons are retained. The returned data is not applied to domain records or authorization by this runner: that remains the selected adapter's explicit pretransaction policy.

type HookRunnerConfig

type HookRunnerConfig struct {
	Fields          []HookFieldRegistration
	Reasons         []PolicyReason
	MaximumDuration time.Duration
}

HookRunnerConfig is copied immutable composition: at most64 fields and32 unique reason codes, and a positive maximum duration no greater than1 minute. Secret fields and lists are unavailable. Hooks must honor context deadlines; execution is synchronous, with no goroutine attempting forced preemption.

type IdempotencyKey

type IdempotencyKey struct {
	// contains filtered or unexported fields
}

IdempotencyKey owns a transient immutable client retry key with no raw accessor. Zero is invalid; copies are comparable and safe for concurrent digesting. Equality identifies the same constructed owner, not equal bytes; independently constructed keys may be correlated only by scoped digests. Retain only keyed digests in durable mappings. Dropping references does not guarantee erasure of immutable strings or caller-owned copies.

func NewIdempotencyKey

func NewIdempotencyKey(raw []byte) (IdempotencyKey, error)

NewIdempotencyKey copies exactly 1..256 ASCII HTTP token bytes: letters, digits and !#$%&'*+-.^_`|~. Spaces, commas, quotes, controls, non-ASCII bytes and separators deny. There is no trimming, case folding or normalization. InvalidInputError is redacted. HTTP adapters separately reject duplicate headers and keys supplied anywhere other than the single permitted header. This pure constructor performs no I/O or authentication.

Example
package main

import (
	"fmt"
	identity "github.com/faustbrian/go-identity"
)

func main() {
	// The HTTP adapter supplies exactly one validated Idempotency-Key header;
	// direct API consumers construct the same opaque exact-byte value.
	key, err := identity.NewIdempotencyKey([]byte("example_retry_key"))
	if err != nil {
		panic(err)
	}
	fmt.Println(key)
}
Output:
identity.IdempotencyKey(redacted)

func (IdempotencyKey) Digest

func (k IdempotencyKey) Digest(authorityKey [32]byte) ([32]byte, error)

Digest returns HMAC-SHA-256 under a nonzero authority-owned 32-byte lookup key over "golib.identity.request-key.v1\x00", unsigned 64-bit big-endian raw length and exact raw bytes. It conveys no authorization or command outcome. The consuming adapter owns tenant/actor/method/route scoping and immutable lookup-key namespace; rotating this key cannot silently reassign old retries. Zero client or authority keys return redacted InvalidInputError. No I/O runs.

func (IdempotencyKey) Format

func (IdempotencyKey) Format(s fmt.State, _ rune)

Format never reveals raw client bytes for any diagnostic verb.

func (IdempotencyKey) LogValue

func (IdempotencyKey) LogValue() slog.Value

LogValue emits a constant independent of client bytes or validity.

func (IdempotencyKey) MarshalJSON

func (IdempotencyKey) MarshalJSON() ([]byte, error)

MarshalJSON rejects implicit key persistence or HTTP disclosure.

func (IdempotencyKey) MarshalText

func (IdempotencyKey) MarshalText() ([]byte, error)

MarshalText rejects text and map-key serialization.

type Identifier

type Identifier struct {
	ID             IdentifierID
	UserID         UserID
	Kind           IdentifierKind
	CanonicalValue string
	DisplayValue   string
	Verified       bool
	Primary        bool
	Version        uint64
	// NormalizationProfile and NormalizationVersion preserve the exact policy
	// that produced CanonicalValue. Both absent denotes an existing unbound
	// record, never permission to reinterpret it using today's policy.
	// Omit absent bindings to preserve canonical JSON in existing authenticated
	// signup-input envelopes; adding zero fields would make their byte check fail.
	NormalizationProfile string `json:",omitempty"`
	NormalizationVersion uint32 `json:",omitempty"`
}

Identifier is an identity-owned directory record. Tenant scope is inherited from its enclosing authoritative User partition, never inferred from UserID. Values must already be canonical under the selected versioned feature policy; structural validation does not normalize or prove uniqueness/verification. Primary is per kind and requires verification. Treat snapshots as immutable.

func (Identifier) Format

func (Identifier) Format(s fmt.State, verb rune)

Format prevents canonical and display identifiers from entering diagnostics.

func (Identifier) Validate

func (i Identifier) Validate(user UserID) error

Validate checks the exact user, closed kind, bounded values and positive record version. It confers no authority and does not accept a claimed primary without verification. Canonical/display text is at most 512 UTF-8 bytes with no C0/C1 controls; provider-subject spaces are preserved, not trimmed.

type IdentifierAccessPlan

type IdentifierAccessPlan struct {
	Mutation             IdentifierMutation
	RemainingUsablePaths uint16
}

IdentifierAccessPlan is a copied provisional mutation and bounded count of surviving usable human login paths, not distinct credentials. It grants no permission or durable outcome. A successful decision never consumes proof.

func CheckIdentifierAccess

func CheckIdentifierAccess(snapshot IdentifierAccessSnapshot, change IdentifierChange, now time.Time) (IdentifierAccessPlan, error)

CheckIdentifierAccess computes the identifier transition and rejects any resulting loss of all human login paths. Call only after authorization, and repeat under durable owner locks with current credential/factor/provider eligibility and one trusted clock sample. Independent methods survive; identifier-bound methods need the same canonical value and required successor verification/primary state. Expiry is exclusive; API keys never count. Malformed/foreign/duplicate paths return InvalidInput, stale dependency versions return Conflict, and no surviving path returns FinalAccessError. Other transition errors retain ApplyIdentifierChange semantics. All errors return a zero plan; inputs are not mutated. No IO or authority refresh occurs. Pre-auth registration must use its distinct workflow, not this current-human access decision before a login method exists.

func (IdentifierAccessPlan) Format

func (IdentifierAccessPlan) Format(s fmt.State, _ rune)

Format redacts the provisional mutation and enumeration-sensitive count.

type IdentifierAccessSnapshot

type IdentifierAccessSnapshot struct {
	State    IdentifierState
	Accounts []Account
	Methods  []IdentifierLoginMethod
}

IdentifierAccessSnapshot is a complete consistent primary view for the pure final-access decision. Methods contains at most 128 distinct login paths; Accounts has AccountSnapshot semantics. The durable owner must establish current method eligibility and lock every applicable owner before constructing this view. It is neither an authority proof nor a reusable validity lease. Treat all input collections as immutable during a call.

func (IdentifierAccessSnapshot) Format

func (IdentifierAccessSnapshot) Format(s fmt.State, _ rune)

Format prevents identifiers, account subjects and credential linkage leaking.

type IdentifierAction

type IdentifierAction uint8

IdentifierAction selects one pure domain transition, never authorization.

const (
	IdentifierAttach IdentifierAction = iota + 1
	IdentifierVerify
	IdentifierMakePrimary
	IdentifierReplace
	IdentifierRemove
	// IdentifierRestartVerification advances only an existing pending,
	// non-primary verification generation. Native resend must authorize fresh
	// proof and atomically replace delivery/capability custody; generic identifier
	// commands do not expose this feature-specific transition.
	IdentifierRestartVerification
	// IdentifierVerifyPrimary verifies one pending email and selects it as
	// primary in one native transition. The feature owner must prove original
	// new-address possession and current change policy; generic identifier
	// commands do not expose this compound action.
	IdentifierVerifyPrimary
	// IdentifierRename changes an already comparison-bound username only. A
	// display-only change preserves possession; a new canonical value retires
	// the predecessor and resets verification like replacement. Native username
	// admission must supply fresh proof and the old-name cooldown policy.
	IdentifierRename
	// IdentifierReplaceVerifiedPhone is a feature-only atomic replacement after
	// actual new-number OTP consumption. It preserves primary selection and
	// retires the original value while advancing each version exactly once.
	// Generic identifier commands do not expose this action; the native phone
	// owner must bind proof, current authority, OTP and all required effects.
	IdentifierReplaceVerifiedPhone
)

type IdentifierChange

type IdentifierChange struct {
	TenantID                    TenantID
	UserID                      UserID
	Action                      IdentifierAction
	ID                          IdentifierID
	Kind                        IdentifierKind
	CanonicalValue              string
	DisplayValue                string
	NormalizationProfile        string `json:",omitempty"`
	NormalizationVersion        uint32 `json:",omitempty"`
	ExpectedUserVersion         uint64
	ExpectedAuthorityVersion    uint64
	ExpectedIdentifierVersion   uint64
	ExpectedVerificationVersion uint64
}

IdentifierChange is bounded intent and exact optimistic fences. Attach, Replace, Rename and ReplaceVerifiedPhone supply Kind/CanonicalValue/DisplayValue; other actions omit them. Attach must omit target versions; other actions require both exact target versions. Replacement preserves ID/kind, resets verification and primary, and retires the old value. Rename requires the exact existing username comparison binding and preserves verification for display-only edits. Trusted feature owners establish canonicalization, proof and reuse policy; client input must never directly reach ApplyIdentifierChange.

func (IdentifierChange) Format

func (IdentifierChange) Format(s fmt.State, verb rune)

Format redacts candidate canonical/display values.

type IdentifierChangeRequest

type IdentifierChangeRequest struct {
	CommandID      CommandID
	Change         IdentifierChange
	Proof          capability.Proof
	SessionID      string
	SessionVersion uint64
	ActorScope     authorization.DecisionContext
	CorrelationID  string
}

IdentifierChangeRequest is trusted current-human input, not public JSON. Preserve the exact command/proof/session/version and intent across retries. Canonical values come from the fixed, versioned owning feature policy, not client casing or the Go toolchain. Feature pre-auth flows remain separate.

type IdentifierCommand

type IdentifierCommand struct {
	Binding        CommandBinding
	Change         IdentifierChange
	Proof          capability.Grant
	SessionID      string
	SessionVersion uint64
}

IdentifierCommand is immutable intent for the sole durable identity owner. Verified proof comes from the configured identity signer only after real session, canonicalization and feature challenge/reuse checks. Construction grants no such authority. The owner repeats fresh policy, session, proof, uniqueness, cooldown and final-access checks under locks before consumption.

func (IdentifierCommand) Format

func (IdentifierCommand) Format(s fmt.State, verb rune)

Format redacts canonical/display values and the proof envelope.

type IdentifierConfig

type IdentifierConfig struct {
	Query                      UserQueryConfig
	ProofIssuer, ProofAudience string
	MaximumProofAge            time.Duration
}

IdentifierConfig freezes query policy and exact signed-proof authority. MaximumProofAge is positive and at most five minutes. It does not establish current credential-specific reauthentication or challenge verification.

type IdentifierID

type IdentifierID string

IdentifierID is an exact case-sensitive record ID, separate from an email, phone, username or provider subject. Empty is invalid; at most 128 UTF-8 bytes are accepted. Imported canonical IDs are preserved unchanged.

func ParseIdentifierID

func ParseIdentifierID(raw string) (IdentifierID, error)

ParseIdentifierID validates structure without granting ownership or authority.

type IdentifierKind

type IdentifierKind string

IdentifierKind selects an exact indexed identifier namespace. Empty means no identifier filter, not a wildcard namespace for a supplied value.

const (
	IdentifierEmail           IdentifierKind = "email"
	IdentifierPhone           IdentifierKind = "phone"
	IdentifierUsername        IdentifierKind = "username"
	IdentifierProviderSubject IdentifierKind = "provider-subject"
	// IdentifierAnonymousPlaceholder is the reserved non-contact namespace owned
	// by anonymous identity lifecycle. It is never verified, primary, deliverable
	// or a generic identifier mutation target. Ordinary email lookup cannot match it.
	IdentifierAnonymousPlaceholder IdentifierKind = "anonymous-placeholder"
)

func (IdentifierKind) Valid

func (k IdentifierKind) Valid() bool

Valid recognizes a persisted identifier kind. Empty is only an omitted list filter, never a valid kind for an identifier record or mutation.

type IdentifierLoginMethod

type IdentifierLoginMethod struct {
	Method                                 LoginMethod
	IdentifierID                           IdentifierID
	IdentifierVersion, VerificationVersion uint64
	RequiresVerified, RequiresPrimary      bool
}

IdentifierLoginMethod is one authority-owner-evaluated human login path. Method.Usable includes current credential, policy, factor and provider checks; a caller boolean is not authority. An optional identifier dependency names its exact current record and verification versions. Empty IdentifierID denotes an independent path and requires zero versions and false requirements. Different identifiers may provide alternative paths for the same exact credential. RequiresPrimary/RequiresVerified come from the selected login policy, never client input. Replacement never inherits access to the old canonical value.

func (IdentifierLoginMethod) Format

func (IdentifierLoginMethod) Format(s fmt.State, _ rune)

Format redacts login-path linkage and eligibility observations.

type IdentifierMutation

type IdentifierMutation struct {
	State               IdentifierState
	Retired             *Identifier
	RetiredVerification *Verification
}

IdentifierMutation is privately copied proposed state, not a committed outcome. Retired and RetiredVerification are present only for replacement, canonical-changing rename or removal. Removal advances their versions and revokes verification; replacement/rename retain the original pair for old-value reuse tombstone creation. Display-only rename retires nothing. The adapter must derive a scoped keyed non-reversible reuse digest, never persist canonical values in plaintext tombstones/events/audit. Legal storage of current directory values remains a separate encrypted storage policy.

func ApplyIdentifierChange

func ApplyIdentifierChange(state IdentifierState, change IdentifierChange, now time.Time) (IdentifierMutation, error)

ApplyIdentifierChange computes an all-or-nothing copied next state without I/O, goroutines, locks, proof verification, clock reads or input mutation. Active users only; overflow and stale versions deny. Each successful change advances user aggregate and identifier authority exactly once. Primary switching bumps both changed records without renewing verification time. Same-value replacement and repeated verify/primary are conflicts, not replay.

The platform durable owner must check exact same-command replay first, lock the full partition and tenant uniqueness index, validate current operation authorization/canonical policy, establish proof/version/session freshness, enforce final usable access and old-value cooldown/reuse policy, then consume applicable one-use proof/risk and persist state, tombstones, lifecycle events, audit and original receipt atomically. This function proves none of those durable/authority claims. Verify is usable only after the feature's real subject/value/version-bound challenge succeeds; a caller boolean is not proof. Errors return zero mutation. now is one trusted owner sample; newly persisted verification time is UTC truncated to microseconds, never a new proof lifetime.

Example
package main

import (
	"fmt"
	"time"

	identity "github.com/faustbrian/go-identity"
)

func main() {
	// The platform owner supplies locked primary state. This pure computation
	// does not authorize a caller, verify a challenge or commit durable state.
	state := identity.IdentifierState{
		User:             identity.User{ID: "user", TenantID: "tenant", Status: identity.StatusActive, Version: 1},
		AuthorityVersion: 1,
	}
	next, err := identity.ApplyIdentifierChange(state, identity.IdentifierChange{
		TenantID: "tenant", UserID: "user", ID: "email-record",
		Action: identity.IdentifierAttach, Kind: identity.IdentifierEmail,
		CanonicalValue: "person@example.test", DisplayValue: "Person@example.test",
		ExpectedUserVersion: 1, ExpectedAuthorityVersion: 1,
	}, time.Date(2026, 10, 2, 8, 0, 0, 0, time.UTC))
	if err != nil {
		panic(err)
	}
	fmt.Println(next.State.AuthorityVersion, next.State.Identifiers[0].Verified, next.State.Identifiers[0].Primary)
}
Output:
2 false false

func ApplyIdentifierRemovalWithPrimary

func ApplyIdentifierRemovalWithPrimary(state IdentifierState, change IdentifierChange, selection IdentifierPrimarySelection, now time.Time) (IdentifierMutation, error)

ApplyIdentifierRemovalWithPrimary copies one removal and explicit primary promotion into a single proposed transition. The removed record must be the current primary; its replacement must be a distinct, already-verified record of the same kind in the same partition with exact non-overflowing versions. User/authority advance once; promotion advances only the replacement record, never its verification version or time. Errors return zero and inputs remain untouched. No IO, clock sampling, authority, one-use or durable effect occurs. A native feature must bind the selection in its original intent, lock/recheck the complete partition and persist proof/session/audit/lifecycle effects with this mutation. IdentifierIntentDigest still binds only the base change.

func (IdentifierMutation) Format

func (IdentifierMutation) Format(s fmt.State, verb rune)

Format redacts proposed state and retired identifier values.

type IdentifierPrimarySelection

type IdentifierPrimarySelection struct {
	ID                          IdentifierID
	ExpectedIdentifierVersion   uint64
	ExpectedVerificationVersion uint64
}

IdentifierPrimarySelection names one exact existing verified record and its independent versions. Zero is not a selection. It grants no authority and cannot select a subject, change a value or renew verification freshness.

func (IdentifierPrimarySelection) Format

Format omits the selected identifier's personal correlation.

func (IdentifierPrimarySelection) Validate

func (s IdentifierPrimarySelection) Validate() error

Validate checks bounded identifier and independent version syntax only. It performs no lookup or authorization; zero and overflowing versions deny.

type IdentifierProjection

type IdentifierProjection struct {
	// contains filtered or unexported fields
}

IdentifierProjection is an opaque, bounded snapshot, not authentication, ownership, permission, a commit receipt or authoritative freshness evidence. Its zero is invalid. NewIdentifierProjection is the only construction path; reconstructing it from caller-supplied metadata never proves a primary read. Copies support concurrent immutable reads and own no resources or goroutines. Value omission is permanent for that projection: no hidden mailbox is retained.

func NewIdentifierProjection

func NewIdentifierProjection(s IdentifierProjectionSpec) (IdentifierProjection, error)

NewIdentifierProjection validates expected tenant/user/record, complete ordered partition consistency and the explicit output selection before copying one identifier. It preserves canonical/display text, imported IDs and all versions unchanged; it performs no normalization, policy callback, IO or transition. Primary requires verification; pending/revoked values are valid non-primary snapshots. InvalidInputError is redacted and returns an invalid zero value. The calling feature separately owns authorization, disclosure, primary read and commit-outcome checks. No timestamp here can substitute for those checks.

Example
package main

import (
	"fmt"
	"time"

	identity "github.com/faustbrian/go-identity"
)

func main() {
	// A trusted feature has already authorized and read this consistent partition.
	observed := time.Date(2026, 10, 10, 19, 0, 0, 0, time.UTC)
	state := identity.IdentifierState{
		User:             identity.User{ID: "user-one", TenantID: "tenant-one", Status: identity.StatusActive, Version: 2},
		AuthorityVersion: 2,
		Identifiers: []identity.Identifier{{
			ID: "email-one", UserID: "user-one", Kind: identity.IdentifierEmail,
			CanonicalValue: "member@example.test", Verified: true, Version: 2,
		}},
		Verifications: []identity.Verification{{
			IdentifierID: "email-one", State: identity.VerificationVerified,
			VerifiedAt: observed.Add(-time.Minute), Version: 2,
		}},
	}
	projection, err := identity.NewIdentifierProjection(identity.IdentifierProjectionSpec{
		TenantID: "tenant-one", UserID: "user-one", IdentifierID: "email-one",
		State: state, ObservedAt: observed, Values: identity.IdentifierValuesOmitted,
	})
	if err != nil {
		panic(err)
	}
	metadata, err := projection.Bindings()
	if err != nil {
		panic(err)
	}
	_, _, hasValues := projection.Values()
	fmt.Println(metadata.Verified, metadata.IdentifierVersion, hasValues)
}
Output:
true 2 false

func (IdentifierProjection) Bindings

Bindings returns an owned metadata copy or a redacted InvalidInputError for zero. It returns no address text and proves neither current access nor commit.

func (IdentifierProjection) Format

func (IdentifierProjection) Format(s fmt.State, _ rune)

Format redacts personal metadata and values for all diagnostic verbs.

func (IdentifierProjection) LogValue

func (IdentifierProjection) LogValue() slog.Value

LogValue prevents implicit structured-log disclosure.

func (IdentifierProjection) MarshalJSON

func (IdentifierProjection) MarshalJSON() ([]byte, error)

MarshalJSON refuses implicit export; the feature owns explicit response fields.

func (IdentifierProjection) MarshalText

func (IdentifierProjection) MarshalText() ([]byte, error)

MarshalText refuses implicit text and map-key disclosure.

func (IdentifierProjection) Values

func (p IdentifierProjection) Values() (canonical, display string, present bool)

Values explicitly reveals the selected canonical/display values when they were included at construction. Zero and omitted projections return empty strings and false. The returned immutable strings are sensitive and must not be used as authority, logged, or copied into unprotected output.

type IdentifierProjectionBindings

type IdentifierProjectionBindings struct {
	TenantID            TenantID
	UserID              UserID
	IdentifierID        IdentifierID
	Kind                IdentifierKind
	Status              Status
	Verified            bool
	Primary             bool
	VerificationState   VerificationState
	UserVersion         uint64
	AuthorityVersion    uint64
	IdentifierVersion   uint64
	VerificationVersion uint64
	VerifiedAt          time.Time
	ObservedAt          time.Time
	Values              IdentifierValueVisibility
}

IdentifierProjectionBindings is a caller-owned immutable-by-convention copy of one observation's metadata. Versions are distinct positive dimensions and are never incremented by projection. VerifiedAt retains the original history; ObservedAt does not make it current authority. IDs and verification metadata remain personal data. Explicit output serialization belongs to the feature's authorized no-store response, not logs, caches or authorization decisions.

type IdentifierProjectionSpec

type IdentifierProjectionSpec struct {
	TenantID     TenantID
	UserID       UserID
	IdentifierID IdentifierID
	State        IdentifierState
	Values       IdentifierValueVisibility
	ObservedAt   time.Time
}

IdentifierProjectionSpec selects one row from a consistent, already-authorized primary partition. TenantID and UserID are the expected query/command scope, not scope inferred from the row. State may contain pending or revoked records and blocked users for authorized administrative inspection. ObservedAt is the owner's trusted observation sample, not a lease or proof of current validity. Construction validates the complete partition, not only the selected row. Keep the input slices immutable during construction; they are not retained. Explicit fields are sensitive; do not put the spec into diagnostic containers.

func (IdentifierProjectionSpec) Format

func (IdentifierProjectionSpec) Format(s fmt.State, _ rune)

Format redacts the complete sensitive source partition and selection.

func (IdentifierProjectionSpec) LogValue

func (IdentifierProjectionSpec) LogValue() slog.Value

LogValue prevents implicit structured-log disclosure of constructor input.

func (IdentifierProjectionSpec) MarshalJSON

func (IdentifierProjectionSpec) MarshalJSON() ([]byte, error)

MarshalJSON refuses implicit source-partition export.

func (IdentifierProjectionSpec) MarshalText

func (IdentifierProjectionSpec) MarshalText() ([]byte, error)

MarshalText refuses implicit constructor-input and map-key disclosure.

type IdentifierProofSpec

type IdentifierProofSpec struct {
	Command              IdentifierCommand
	Issuer, Audience, ID string
	IssuedAt, ExpiresAt  time.Time
}

IdentifierProofSpec is unsigned trusted ceremony output, never authorization or caller-asserted verification. Command.Proof must be zero. The configured signer may issue only after exact feature proofs and current policy succeed.

type IdentifierQuery

type IdentifierQuery struct {
	TenantID           TenantID
	UserID             UserID
	MaximumIdentifiers uint16
}

IdentifierQuery is an exact tenant/user complete primary read. More than MaximumIdentifiers is unavailable, not silently truncated or guessed absent.

type IdentifierReceipt

type IdentifierReceipt struct {
	IdentifierID                               IdentifierID
	Action                                     IdentifierAction
	ExpectedAuthorityVersion, AuthorityVersion uint64
	// IntentDigest is the exact original IdentifierIntentDigest, never current
	// authority or a published private-value index. Retain it only in the
	// protected immutable receipt; public results never disclose it.
	IntentDigest [32]byte
}

IdentifierReceipt records committed local admission, never current authority or feature challenge/provider success. It contains no identifier values. Identifier authority advances once; the owner also persists lifecycle events and required cascade admission. Terminal cleanup is not inferred from this.

func (IdentifierReceipt) Format

func (IdentifierReceipt) Format(s fmt.State, verb rune)

Format redacts retained intent fingerprints as well as record metadata.

type IdentifierRecoveryRequest

type IdentifierRecoveryRequest struct {
	CommandID    CommandID
	TenantID     TenantID
	UserID       UserID
	IdentifierID IdentifierID
	Action       IdentifierAction
	ActorScope   authorization.DecisionContext
}

IdentifierRecoveryRequest authorizes exact original record/action scope. Possession of a command ID alone never authorizes receipt disclosure.

type IdentifierRepository

type IdentifierRepository interface {
	GetIdentifierState(context.Context, IdentifierQuery) (IdentifierState, error)
}

IdentifierRepository is platform-owned, concurrency-safe primary storage. Reads must honor the bounded context, return a consistent full partition and ErrUserAbsent for absence. A cache or consumer-supplied store is not authority.

type IdentifierResult

type IdentifierResult struct {
	CommandID                 CommandID
	CommitOutcome             CommitOutcome
	IdentifierID              IdentifierID
	Version, AuthorityVersion uint64
}

IdentifierResult keeps known commitment even when disclosure fails. Zero versions/ID on an error do not imply rollback; unknown requires scoped recovery.

type IdentifierService

type IdentifierService struct {
	// contains filtered or unexported fields
}

IdentifierService owns identity command admission, not sessions, challenges, normalization engines, transactions, caches or goroutines. All dependencies must be concurrency-safe and honor bounded contexts. Use trusted composition.

func NewIdentifierService

NewIdentifierService validates and freezes policy without external I/O.

func (*IdentifierService) IdentifierChange

func (s *IdentifierService) IdentifierChange(ctx context.Context, r IdentifierChangeRequest) (out IdentifierResult, err error)

IdentifierChange authorizes before proof/primary access and again against current state. The sole owner checks replay before CAS, repeats locked fresh evidence/uniqueness/final access and atomically consumes proof/risk with state, keyed reuse tombstones, lifecycle events/audit and receipt. This service owns no durable reservation and cannot claim atomicity from a primary read alone.

func (*IdentifierService) RecoverIdentifierChange

func (s *IdentifierService) RecoverIdentifierChange(ctx context.Context, r IdentifierRecoveryRequest) (out IdentifierResult, err error)

RecoverIdentifierChange reads only the exact original receipt under current record/action/owner policy; no Execute, proof consumption or challenge runs.

type IdentifierState

type IdentifierState struct {
	User             User
	Identifiers      []Identifier
	Verifications    []Verification
	AuthorityVersion uint64
}

IdentifierState is a complete, consistent primary tenant/user partition at the mutation lock boundary. Identifiers are strictly ID-ordered, at most 128; Verifications correspond positionally and exactly. AuthorityVersion is the tenant/user identifier dimension, not the user-authority dimension. All versions are positive. Do not mutate input collections during a call.

func (IdentifierState) Format

func (IdentifierState) Format(s fmt.State, verb rune)

Format redacts the complete primary directory projection.

type IdentifierValueVisibility

type IdentifierValueVisibility uint8

IdentifierValueVisibility is a closed output selection, not permission to inspect identifiers. Zero is invalid. The trusted feature owner must obtain current authorization and redaction policy before selecting either value.

const (
	// IdentifierValuesOmitted retains metadata but no canonical or display value.
	IdentifierValuesOmitted IdentifierValueVisibility = iota + 1
	// IdentifierValuesIncluded permits explicit Values access to the selected row.
	IdentifierValuesIncluded
)

type IdentitySecurityEventV1

type IdentitySecurityEventV1 struct {
	// contains filtered or unexported fields
}

IdentitySecurityEventV1 is the shared immutable profile builder. It owns no persistence or goroutine. The borrowed wall clock must be concurrency-safe; Build performs no callbacks under locks or database transactions. Copies share immutable primitive builders. The zero value is invalid.

func NewIdentitySecurityEventV1

func NewIdentitySecurityEventV1(wall clock.Clock) (*IdentitySecurityEventV1, error)

NewIdentitySecurityEventV1 selects exact profile limits and secure random record identities. A nil wall clock rejects; its panic/zero time is handled by the audit primitive. Caller retains clock ownership. No globals are changed.

func (*IdentitySecurityEventV1) Build

Build returns an immutable, explicitly redacted canonical audit.Record with no structured mutation. It does not persist, buffer, retry or grant authority. Unsupported fields/actions and unsafe attribution return the stable sentinel; cancellation retains its identity and returns no record. RecordedAt belongs to the primitive builder clock, separately from trusted OccurredAt.

func (*IdentitySecurityEventV1) BuildDelivery

BuildDelivery builds the delivery subset of the shared immutable security profile. It never persists, retries or grants authority. OutcomeSucceeded means local bookkeeping was recorded; ambiguous provider evidence is a mark_unknown event, not OutcomeUnknown (reserved for an unknown commit). Accepted/retry/expiry/exhaustion/erasure do not claim provider confirmation. Exhaustion is local policy evidence and emits no provider-result assertion. Caller owns the bounded database transaction when it stages the result.

func (*IdentitySecurityEventV1) BuildRiskDecision

BuildRiskDecision constructs the risk-decision subset of identity.security.v1 using the existing canonical audit record. Allow maps to succeeded/completed; deny, throttle and step-up map to denied with their closed registered reason. No provider attribution is fabricated and no structured mutation is asserted. Only user and genuine pre-auth attempt subjects are supported in this subset. Construction does not deliver audit, grant permission or prove a state commit. Nil/zero owners, malformed attribution and unknown decisions reject with ErrInvalidSecurityEvent and no record; context errors retain their identity. Concurrency and clock ownership follow IdentitySecurityEventV1.

type InProgressError

type InProgressError struct {
	CommandID  CommandID
	RetryAfter time.Duration
}

InProgressError is a bounded nonterminal observation of the same command.

func (InProgressError) Error

func (InProgressError) Error() string

type InvalidInputError

type InvalidInputError struct{ Field string }

InvalidInputError classifies an invalid identity input without retaining its value. Field is a fixed schema field name, never user-supplied data. Error's text is deliberately constant, including for cross-tenant mismatches.

func (InvalidInputError) Error

func (InvalidInputError) Error() string

Error returns the stable redacted validation diagnostic.

type LifecycleCascadeAdmission

type LifecycleCascadeAdmission struct {
	CascadeID           CascadeID
	Generation          uint64
	Kind                LifecycleCascadeKind
	TenantID            TenantID
	UserID              UserID
	IdentifierID        IdentifierID
	AuthorityVersion    uint64
	ManifestVersion     string
	Status              RevocationStatus
	Owners              []RevocationOwnerReceipt
	ExternalLimitations []PolicyReason
}

LifecycleCascadeAdmission is a historical pending owner admission, never authentication authority or asynchronous completion. Generation is initially one; AuthorityVersion is the identifier dimension for removal and change. Owners retains the exact manifest including unavailable consumers. Scope IDs are pseudonymous personal data; explicit JSON is for protected receipt storage or authorized projection. Treat values as immutable; Clone copies collections.

func (LifecycleCascadeAdmission) Clone

Clone returns caller-owned collections without changing admission identity.

func (LifecycleCascadeAdmission) Format

func (LifecycleCascadeAdmission) Format(s fmt.State, _ rune)

Format omits pseudonymous scope and historical command correlation.

func (LifecycleCascadeAdmission) Validate

Validate checks exact pending admission shape and command scope, not database durability, current authority or consumer acknowledgement. Self and explicitly authorized administrative removal retain the target's scope, not the actor's. Native command admission owns authorization of a distinct human actor. Noncommitted outcomes must not carry this receipt; CommandResult enforces that distinction.

type LifecycleCascadeKind

type LifecycleCascadeKind string

LifecycleCascadeKind names a selected identity-owned cascade, not completion. Unknown kinds deny until their exact scope and consumer contract is supported.

const (
	// IdentifierRemovalCascade starts cleanup after the identifier authority
	// mutation. Old-address authentication must already deny at admission.
	IdentifierRemovalCascade LifecycleCascadeKind = "lifecycle.cascade.identifier_remove"
	// IdentifierChangeCascade starts cleanup after atomic identifier replacement.
	// The old value is already retired and cannot authenticate during cleanup.
	IdentifierChangeCascade LifecycleCascadeKind = "lifecycle.cascade.identifier_change"
)

type ListUsersQuery

type ListUsersQuery struct {
	TenantID  TenantID
	Filters   UserListFilters
	Cursor    Cursor
	Limit     uint32
	Fields    []FieldName
	ExpiresAt time.Time
}

ListUsersQuery selects one authoritative stable snapshot. Users are ordered by the bytewise ascending canonical UserID, with no duplicates. A continuation must retain that snapshot and filters even when live directory state changes. Repositories must retain snapshots through ExpiresAt, scope them to TenantID and Filters, fail closed on expiry, and honor cancellation. Collections are call-owned; implementations must not mutate inputs or retain their aliases.

type LoginMethod

type LoginMethod struct {
	Credential CredentialRef
	AccountID  AccountID
	Usable     bool
	ExpiresAt  time.Time
}

LoginMethod is a primary authority-owned eligibility observation, never a caller assertion. Usable includes verification, enablement, policy/factor and provider availability. ExpiresAt zero means indefinite; otherwise expiry is exclusive. AccountID is mandatory only for provider methods. API keys never count as a replacement for a human login method, even if marked usable.

type MapClaimsFunc

MapClaimsFunc is a concurrent-safe bounded pure verified-claim mapping.

type NormalizationPolicy

type NormalizationPolicy string

NormalizationPolicy names a versioned composition-owned normalization rule. Identity delegates these rules; it does not own another Unicode/phone engine.

const (
	NormalizeNone       NormalizationPolicy = "none"
	NormalizeUnicodeNFC NormalizationPolicy = "unicode-nfc"
	NormalizeCaseFold   NormalizationPolicy = "case-fold"
	NormalizeEmail      NormalizationPolicy = "email"
	NormalizePhone      NormalizationPolicy = "phone"
)

type NormalizeAttributeFunc

type NormalizeAttributeFunc func(context.Context, NormalizationPolicy, AttributeScalar) (AttributeScalar, error)

NormalizeAttributeFunc normalizes one admitted string scalar. The injected implementation must use stable versioned rules, preserve kind, honor context and be side-effect-free/concurrent-safe. Errors and panics are redacted.

type NotCommittedError

type NotCommittedError struct{ CommandID CommandID }

NotCommittedError identifies authoritative abort/absence, never inferred rollback.

func (NotCommittedError) Error

func (NotCommittedError) Error() string

type OperationContext

type OperationContext struct {
	OperationID   string
	TenantID      TenantID
	ActorID       ActorID
	CorrelationID string
	Deadline      time.Time
}

OperationContext is immutable operation correlation, never authentication or transaction authority. Deadline, when present, is the original exclusive execution bound; hook execution may narrow but never extend that instant.

type PolicyEvaluator

type PolicyEvaluator struct {
	// contains filtered or unexported fields
}

PolicyEvaluator validates copied typed input and output around the existing pure callbacks. It owns no authority, state, clock, locks or goroutines. Invalid input never reaches a callback. Errors/panics/cancellation/malformed decisions yield zero output; the evaluator never retries or substitutes policy.

func NewPolicyEvaluator

func NewPolicyEvaluator(set PolicySet, cfg PolicyEvaluatorConfig) (*PolicyEvaluator, error)

NewPolicyEvaluator freezes composition without I/O, rejecting missing members.

func (*PolicyEvaluator) AssessRisk

AssessRisk validates the exact bound operation and returns no partial decision after failure. The valid RiskDeny/StepUp variants remain explicit outcomes.

Example
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/faustbrian/go-authorization/v3"
	"github.com/faustbrian/go-identity"
)

// Example composition denies authorization. Real workflows must separately
// require the upstream service's current Allow; RiskAllow is not that grant.
type policyExampleAuthorizer struct{}

func (policyExampleAuthorizer) Decide(context.Context, authorization.Request) (authorization.Decision, error) {
	return authorization.Decision{Outcome: authorization.Deny, Revision: 1}, nil
}

func main() {
	authorize, err := authorization.NewDecisionService(policyExampleAuthorizer{})
	if err != nil {
		panic(err)
	}
	set := identity.PolicySet{
		Authorize: authorize,
		AssessRisk: func(context.Context, identity.RiskPolicyInput) (identity.RiskPolicyDecision, error) {
			return identity.RiskPolicyDecision{Level: identity.RiskLow, Action: identity.RiskAllow, PolicyID: "example", PolicyVersion: 1, Reasons: []identity.PolicyReason{"safe"}}, nil
		},
		MapClaims: func(_ context.Context, in identity.ClaimsPolicyInput) (identity.ClaimsPolicyDecision, error) {
			return identity.ClaimsPolicyDecision{Claims: in.Claims, PolicyID: "example", PolicyVersion: 1, Reason: "safe"}, nil
		},
		DecideRetention: func(context.Context, identity.RetentionPolicyInput) (identity.RetentionPolicyDecision, error) {
			return identity.RetentionPolicyDecision{Disposition: identity.RetentionAllow, PolicyID: "example", PolicyVersion: 1, Reasons: []identity.PolicyReason{"safe"}}, nil
		},
		Redact: func(_ context.Context, in identity.RedactionPolicyInput) (identity.RedactionPolicyDecision, error) {
			return identity.RedactionPolicyDecision{RedactedFields: in.Fields, PolicyID: "example", PolicyVersion: 1, Reason: "safe"}, nil
		},
	}
	evaluator, err := identity.NewPolicyEvaluator(set, identity.PolicyEvaluatorConfig{PolicyID: "example", PolicyVersion: 1, Reasons: []identity.PolicyReason{"safe"}, MaximumDuration: time.Second})
	if err != nil {
		panic(err)
	}
	decision, err := evaluator.AssessRisk(context.Background(), identity.RiskPolicyInput{TenantID: "tenant-example", ActorID: "service-example", OperationID: "identity.user.create", PolicyVersion: 1, Signals: []identity.RiskSignal{}})
	fmt.Println(decision.Action, err)
}
Output:
allow <nil>

func (*PolicyEvaluator) DecideRetention

DecideRetention prevents a destructive allow under a legal hold. Blocking or deferral is a policy observation, never a terminal lifecycle acknowledgement.

func (*PolicyEvaluator) MapClaims

MapClaims returns only a bounded lossless mapping with current policy identity. It does not verify provider signatures or grant any identity authority.

func (*PolicyEvaluator) Redact

Redact requires an exact partition of requested fields. Explicit full denial is valid (all fields redacted); missing/duplicate/unrequested fields are not.

type PolicyEvaluatorConfig

type PolicyEvaluatorConfig struct {
	PolicyID        string
	PolicyVersion   uint64
	Reasons         []PolicyReason
	MaximumDuration time.Duration
}

PolicyEvaluatorConfig registers one immutable policy identity/version and 1..32 distinct safe reason codes (at most 64 bytes). MaximumDuration must be positive and no more than a minute. There are no implicit defaults.

type PolicyReason

type PolicyReason string

PolicyReason is a registered diagnostic code, never user or secret data. Read composition registers at most 32 unique codes, each 1..64 UTF-8 bytes without ASCII whitespace or control characters.

type PolicySet

type PolicySet struct {
	Authorize       authorization.Service
	AssessRisk      AssessRiskFunc
	MapClaims       MapClaimsFunc
	DecideRetention DecideRetentionFunc
	Redact          RedactFunc
}

PolicySet has exactly the five owned composition members. Configure before concurrent use; NewPolicyEvaluator snapshots the callbacks. Authorize remains the exact upstream service, not an identity facade. Callbacks own no stores, transactions, routing or continuation. They must honor context deadlines; no detached goroutine attempts to preempt a misbehaving callback.

type PreAuthTransactionID

type PreAuthTransactionID string

PreAuthTransactionID is a canonical non-zero 128-bit attempt/correlation ID. Its empty zero value is invalid. Immutable values are safe for concurrent reads. Possession grants no authentication, capability-use or transaction authority: consumers must bind the exact tenant, attempt and continuation.

func NewPreAuthTransactionID

func NewPreAuthTransactionID() (PreAuthTransactionID, error)

NewPreAuthTransactionID reads exactly 16 crypto/rand bytes and rejects all-zero entropy. Failure returns an empty ID and ErrPreAuthTransactionIDEntropy.

func ParsePreAuthTransactionID

func ParsePreAuthTransactionID(raw string) (PreAuthTransactionID, error)

ParsePreAuthTransactionID accepts only the canonical encoding of a non-zero 128-bit value, with bounded decoding and ErrInvalidPreAuthTransactionID on failure. Parsing never grants authority or reserves a database transaction.

Example
package main

import (
	"fmt"

	"github.com/faustbrian/go-identity"
)

func main() {
	id, err := identity.ParsePreAuthTransactionID("AQAAAAAAAAAAAAAAAAAAAA")
	if err != nil {
		return
	}
	fmt.Println(identity.FormatPreAuthTransactionID(id))
}
Output:
AQAAAAAAAAAAAAAAAAAAAA

type PrivacyExportCheckpointV1

type PrivacyExportCheckpointV1 struct {
	Major    uint16
	Minor    uint16
	Position uint64
}

PrivacyExportCheckpointV1 is a primary visibility cut, not an aggregate version or timestamp. The initial empty projection commits position 1. Every earlier subject mutation must be visible; producers must retain this exact cut.

type PrivacyExportConfig

type PrivacyExportConfig struct {
	Query                      UserQueryConfig
	ProofIssuer, ProofAudience string
	MaximumProofAge            time.Duration
	EvidenceKeys               []PrivacyExportEvidenceKey
}

PrivacyExportConfig freezes trusted policy, proof trust and retained snapshot keys. Keep historical keys through receipt retention; rotation never renews proof freshness or rewrites snapshot evidence. One to 32 unique keys are required. MaximumProofAge is positive and at most five minutes.

type PrivacyExportContributorBindingV1

type PrivacyExportContributorBindingV1 struct {
	ContributorID  string
	ContractMajor  uint16
	ContractMinor  uint16
	ProjectionID   PrivacyExportProjectionV1
	ScopeKeyDigest [32]byte
	Checkpoint     PrivacyExportCheckpointV1
}

PrivacyExportContributorBindingV1 binds one complete subject cut. Version 1 uses contract/checkpoint 1.0 and the closed scope selector ["subject"] for every contributor: tenant, subject and contributor are separately MAC-bound.

type PrivacyExportContributorCutV1

type PrivacyExportContributorCutV1 struct {
	ContributorID      string
	ProjectionID       PrivacyExportProjectionV1
	CheckpointPosition uint64
}

PrivacyExportContributorCutV1 is trusted owner's captured input. Position is positive; constructing it proves no authoritative read or durable admission.

type PrivacyExportEvidenceKey

type PrivacyExportEvidenceKey struct {
	// contains filtered or unexported fields
}

PrivacyExportEvidenceKey owns a copied versioned independent 256-bit HMAC integrity key. Zero is invalid. Retain versions through snapshot/evidence retention; rotation never re-MACs old records or changes recorded cuts. Key possession is trusted composition authority, never caller-provided config. This value has no destroy/zeroization guarantee in Go; manage retention at the secret owner. Immutable copies are concurrent-safe; never serialize it.

func NewPrivacyExportEvidenceKey

func NewPrivacyExportEvidenceKey(version uint32, material [32]byte) (PrivacyExportEvidenceKey, error)

NewPrivacyExportEvidenceKey accepts positive version and nonzero key bytes. It validates structure, not key entropy, provenance or permission to seal.

func (PrivacyExportEvidenceKey) Format

func (PrivacyExportEvidenceKey) Format(s fmt.State, _ rune)

func (PrivacyExportEvidenceKey) SealSnapshotV1

SealSnapshotV1 copies and authenticates the complete closed input. It performs no storage writes or authorization. Only actual command admission may make an export runnable; callers must not use this constructor to attest locks/cuts.

func (PrivacyExportEvidenceKey) VerifySnapshotV1

VerifySnapshotV1 reconstructs only exact V1 records under their retained key. Missing members, unknown versions, changed checkpoints or any MAC mismatch return zero. It never repairs, reorders, defaults, upgrades or renews evidence.

type PrivacyExportFragmentClassification

type PrivacyExportFragmentClassification string

PrivacyExportFragmentClassification is closed; absence must be proved, not inferred from a missing response, empty data, cancellation or unavailable cut.

const (
	// PrivacyExportFragmentData binds nonempty canonical redacted content.
	PrivacyExportFragmentData PrivacyExportFragmentClassification = "data"
	// PrivacyExportFragmentNotApplicable binds exact absence at the requested cut.
	PrivacyExportFragmentNotApplicable PrivacyExportFragmentClassification = "not-applicable"
)

type PrivacyExportFragmentV1

type PrivacyExportFragmentV1 struct {
	ExportID            PrivacyExportJobID
	TenantID            TenantID
	SubjectID           UserID
	CommandID           CommandID
	PrivacyEpoch        uint64
	EvidenceKeyVersion  uint32
	SnapshotDigest      [32]byte
	ContributorID       string
	ContractMajor       uint16
	ContractMinor       uint16
	ProjectionID        PrivacyExportProjectionV1
	ScopeKeyDigest      [32]byte
	RequestedCheckpoint PrivacyExportCheckpointV1
	ObservedCheckpoint  PrivacyExportCheckpointV1
	SchemaMajor         uint16
	SchemaMinor         uint16
	Classification      PrivacyExportFragmentClassification
	ContentLength       uint64
	ContentDigest       [32]byte
}

PrivacyExportFragmentV1 is exact returned metadata, never publication authority. The contributor owns canonical redaction/content schema and exact-cut reads; the coordinator must validate this binding and independently verify observed bytes before any publication. All schema/contract/checkpoint versions are 1.0. Content itself remains caller-owned, not retained here. Never log this value.

func (PrivacyExportFragmentV1) Format

func (PrivacyExportFragmentV1) Format(s fmt.State, _ rune)

type PrivacyExportJobID

type PrivacyExportJobID string

PrivacyExportJobID is a canonical nonzero 128-bit export identity. Possession grants neither download nor snapshot authority. Empty is invalid.

func NewPrivacyExportJobID

func NewPrivacyExportJobID() (PrivacyExportJobID, error)

NewPrivacyExportJobID reads exactly 16 crypto/rand bytes; failures reveal no ID.

func ParsePrivacyExportJobID

func ParsePrivacyExportJobID(raw string) (PrivacyExportJobID, error)

ParsePrivacyExportJobID accepts only the canonical 22-byte unpadded encoding.

type PrivacyExportProjectionSpecV1

type PrivacyExportProjectionSpecV1 struct {
	ContributorID string
	ProjectionID  PrivacyExportProjectionV1
}

PrivacyExportProjectionSpecV1 declares the immutable projection profile for one contributor. It cannot submit a checkpoint: the durable request owner captures that cut under its admission transaction, never from caller input.

type PrivacyExportProjectionV1

type PrivacyExportProjectionV1 string

PrivacyExportProjectionV1 is a closed, storage-neutral immutable cut profile. Neither value names a table or permits reading mutable current state.

const (
	// PrivacyExportSubjectJournalV1 requires exact subject-scoped append-only reads.
	PrivacyExportSubjectJournalV1 PrivacyExportProjectionV1 = "subject-journal-v1"
	// PrivacyExportStagedSubjectV1 requires a fragment staged in request admission.
	PrivacyExportStagedSubjectV1 PrivacyExportProjectionV1 = "staged-subject-fragment-v1"
)

type PrivacyExportProofSpec

type PrivacyExportProofSpec struct {
	Command              PrivacyExportRequestCommand
	Issuer, Audience, ID string
	IssuedAt, ExpiresAt  time.Time
}

PrivacyExportProofSpec is unsigned trusted ceremony output. Command.Proof must be zero. Supplying it does not confer minting or reauthentication authority.

type PrivacyExportRecoveryRequest

type PrivacyExportRecoveryRequest struct {
	Intent     PrivacyExportRequestCommand
	ActorScope authorization.DecisionContext
}

PrivacyExportRecoveryRequest retains exact unsigned request intent and current authenticated human context. No expired proof is required or consumed.

type PrivacyExportRequest

type PrivacyExportRequest struct {
	Intent     PrivacyExportRequestCommand
	Proof      capability.Proof
	ActorScope authorization.DecisionContext
}

PrivacyExportRequest carries unsigned exact intent, the original opaque proof and authenticated upstream actor context. Intent.Proof must be zero. Binding policy/actor/schema must already match this service, so the signed digest is never silently rewritten. Do not mutate input concurrently during a call.

type PrivacyExportRequestCommand

type PrivacyExportRequestCommand struct {
	Binding                  CommandBinding
	ExpectedIdentityVersion  uint64
	ExpectedAuthorityVersion uint64
	ExpectedPrivacyEpoch     uint64
	SessionID                string
	SessionVersion           uint64
	MaximumBytes             uint64
	ArtifactTTL              time.Duration
	Projections              []PrivacyExportProjectionSpecV1
	Proof                    capability.Grant
}

PrivacyExportRequestCommand binds full-subject portable-JSON export intent. A trusted service must authorize the current human, verify configured proof issuer/audience/freshness and retain the original opaque proof for the durable owner. The owner must repeat those checks under locks, match all expected versions, allocate export ID and the next privacy epoch, capture all cuts, reserve/use the proof atomically, and persist queued admission. Construction does none of that and does not enable a worker or expose data. Concurrent mutation during construction is forbidden; Command owns a copied projection slice. Format is redacted. No arbitrary scope, format, cut or callback exists.

func (PrivacyExportRequestCommand) Format

func (PrivacyExportRequestCommand) Format(s fmt.State, verb rune)

Format prevents signed intent, identifiers and session bindings from leaking.

type PrivacyExportRequestReceipt

type PrivacyExportRequestReceipt struct {
	Status                   PrivacyExportStatus
	Snapshot                 PrivacyExportSnapshotRecordV1
	ExpectedAuthorityVersion uint64
	ExpectedPrivacyEpoch     uint64
	MaximumBytes             uint64
	ArtifactTTL              time.Duration
	IntentDigest             [32]byte
}

PrivacyExportRequestReceipt retains immutable queued-admission metadata in the existing command journal. Snapshot is the complete durable record, not an opaque value that would serialize as an empty object. Treat this mutable reconstruction shape as sensitive, caller-owned data; never log it or mutate it concurrently. Formatting is redacted. Structural validation is not MAC verification, authoritative cut capture, current eligibility or publication. Admission retains the captured user version and advances privacy epoch once; it does not invent an unrelated user/authority-version increment.

func (PrivacyExportRequestReceipt) Format

func (PrivacyExportRequestReceipt) Format(s fmt.State, verb rune)

Format redacts scoped snapshots and retained request metadata for every verb.

type PrivacyExportResult

type PrivacyExportResult struct {
	CommandID     CommandID
	CommitOutcome CommitOutcome
	ExportID      PrivacyExportJobID
	Status        PrivacyExportStatus
}

PrivacyExportResult reports command observation, not publication. Job metadata is returned only for a validated queued admission and authorized disclosure. Known CommitOutcome survives transport, cancellation and disclosure failures.

func (PrivacyExportResult) Format

func (PrivacyExportResult) Format(s fmt.State, verb rune)

Format prevents identifiers from entering diagnostics.

type PrivacyExportService

type PrivacyExportService struct {
	// contains filtered or unexported fields
}

PrivacyExportService admits exact proofs and validates request observations. It owns no storage, locks or goroutines. The platform PostgreSQL UnitOfWork remains the sole durable owner and must check current user/status/authority, real fresh session, legal/destruction eligibility, original proof and all cuts under its locks, atomically consume proof and persist queued admission. Replay and recovery must recheck current observation eligibility. This service cannot make a metadata-only or unimplemented owner safe. Dependencies must honor the bounded context and be concurrent-safe; construction performs no IO.

func NewPrivacyExportService

func NewPrivacyExportService(uow UnitOfWork, caps capability.Service, authorize authorization.Service, redact RedactFunc, c PrivacyExportConfig) (*PrivacyExportService, error)

NewPrivacyExportService validates and copies trusted configuration. It rejects typed-nil dependencies, missing/duplicate retained keys and unbounded policy.

func (PrivacyExportService) Format

func (PrivacyExportService) Format(s fmt.State, verb rune)

Format redacts runtime dependencies and retained integrity-key configuration.

func (PrivacyExportService) MarshalJSON

func (PrivacyExportService) MarshalJSON() ([]byte, error)

MarshalJSON rejects serialization of runtime composition and retained keys.

func (*PrivacyExportService) Recover

Recover queries the same scoped command under current authorization, matching retained unsigned intent and recorded evidence-key version. It never verifies an expired bearer, executes a mutation or allocates a replacement export ID.

func (*PrivacyExportService) Request

Request authorizes the current self-service human before cryptographic work, verifies configured purpose/issuer/audience/age and hands the frozen original proof to the existing durable owner. Invalid owner observations are unknown, never invented rollback. Returned status is queued, never completed/ready.

type PrivacyExportSnapshotRecordV1

type PrivacyExportSnapshotRecordV1 struct {
	SchemaMajor             uint16
	SchemaMinor             uint16
	ExportID                PrivacyExportJobID
	TenantID                TenantID
	SubjectID               UserID
	CommandID               CommandID
	IdentityVersion         uint64
	PrivacyEpoch            uint64
	LifecycleSequence       uint64
	ManifestVersion         uint32
	RequestedAtMicroseconds int64
	Contributors            []PrivacyExportContributorBindingV1
	EvidenceKeyVersion      uint32
	Digest                  [32]byte
}

PrivacyExportSnapshotRecordV1 is caller-owned durable metadata, not a sealed value. VerifySnapshotV1 reconstructs it only after full structural/MAC checks. It contains pseudonymous personal data; do not log or use as authorization.

func (PrivacyExportSnapshotRecordV1) Format

type PrivacyExportSnapshotSpecV1

type PrivacyExportSnapshotSpecV1 struct {
	ExportID                PrivacyExportJobID
	TenantID                TenantID
	SubjectID               UserID
	CommandID               CommandID
	IdentityVersion         uint64
	PrivacyEpoch            uint64
	LifecycleSequence       uint64
	RequestedAtMicroseconds int64
	Cuts                    []PrivacyExportContributorCutV1
}

PrivacyExportSnapshotSpecV1 is structural input, never authority. Trusted admission must capture all cuts while locking the actual identity, rejecting pending destructive state, and persist the complete sealed record atomically. V1 wire IDs require canonical nonzero 128-bit IDs; this does not normalize or rewrite imported directory IDs. An imported-ID profile requires explicit scope/schema reconciliation before admission, never silent conversion. RequestedAtMicroseconds is PostgreSQL UTC within years 1..9999. All counters are positive. Cuts must already be in exact manifest ASCII order.

func (PrivacyExportSnapshotSpecV1) Format

type PrivacyExportSnapshotV1

type PrivacyExportSnapshotV1 struct {
	// contains filtered or unexported fields
}

PrivacyExportSnapshotV1 is immutable integrity evidence, not current authority. It owns copied metadata, no IO, resources or goroutines. Concurrent reads are safe; zero is invalid. A valid MAC cannot prove a cut was read authoritatively, authorization, durable admission, worker eligibility or final publication.

func (PrivacyExportSnapshotV1) Contributor

Contributor returns an immutable exact binding; unknown/unbounded IDs fail.

func (PrivacyExportSnapshotV1) Format

func (PrivacyExportSnapshotV1) Format(s fmt.State, _ rune)

func (PrivacyExportSnapshotV1) Record

Record returns an owned deep copy. Zero returns zero; it cannot be persisted or converted into valid evidence without the complete retained key profile.

func (PrivacyExportSnapshotV1) Valid

func (s PrivacyExportSnapshotV1) Valid() bool

Valid reports construction/reconstruction, not current identity eligibility.

func (PrivacyExportSnapshotV1) ValidateFragment

func (s PrivacyExportSnapshotV1) ValidateFragment(f PrivacyExportFragmentV1, canonical []byte, maximumBytes uint64) error

ValidateFragment checks the complete scope/version-vector binding, declared byte bound and digest of the actual canonical bytes. MaximumBytes is positive and at most 1 GiB. Bounds precede hashing; no bytes are copied or retained. NotApplicable requires zero bytes and SHA-256(empty); Data forbids empty data. This does not prove JSON canonicalization, schema/redaction or exact-cut reads: those are required from the registered contributor and composed coordinator. A valid fragment never authorizes download, publication or cascade closure.

type PrivacyExportStatus

type PrivacyExportStatus string

PrivacyExportStatus is the closed export-job state, not a command commit outcome or proof of object erasure. Empty and unknown strings are invalid. A request receipt is always queued; only the durable worker and lifecycle owners may transition a job through the remaining states.

const (
	// PrivacyExportQueued records admission pending contributor work.
	PrivacyExportQueued PrivacyExportStatus = "queued"
	// PrivacyExportRunning records a leased job, not completed publication.
	PrivacyExportRunning PrivacyExportStatus = "running"
	// PrivacyExportReady requires complete fragments and sealed publication.
	PrivacyExportReady PrivacyExportStatus = "ready"
	// PrivacyExportFailed denies download and requires object reconciliation.
	PrivacyExportFailed PrivacyExportStatus = "failed"
	// PrivacyExportCancelled denies download; it does not attest object erasure.
	PrivacyExportCancelled PrivacyExportStatus = "cancelled"
	// PrivacyExportExpired denies download and outstanding capabilities.
	PrivacyExportExpired PrivacyExportStatus = "expired"
)

type ProfileConfig

type ProfileConfig struct {
	Fields              UserCreationConfig
	AllowedImageOrigins []string
}

ProfileConfig freezes field/time/current-policy composition and 1..32 exact canonical HTTPS image origins. Images are never fetched. No wildcard, userinfo, query, path, fragment or opaque origin is supported. Origins must match URL authority exactly; default ports are not silently equivalent to explicit ones.

type ProfileGetRequest

type ProfileGetRequest struct {
	TenantID TenantID
	Context  authorization.DecisionContext
}

ProfileGetRequest supplies trusted authenticated composition, never client JSON. The current human subject is the only target; resource ID must match it. Session validation is the session/transport owner, not these scope fields.

type ProfileGetResult

type ProfileGetResult struct {
	User    UserProfile
	Version uint64
}

ProfileGetResult is the current permission-filtered self projection.

type ProfileQuery

type ProfileQuery struct {
	TenantID TenantID
	UserID   UserID
	Fields   []FieldName
}

ProfileQuery requests an exact subject/tenant projection from primary state. Fields is bounded and privately owned; no pagination, arbitrary target or cache is allowed. Repositories must return one consistent aggregate snapshot.

type ProfileRecord

type ProfileRecord struct {
	User    User
	Profile UserProfile
}

ProfileRecord is an authoritative internal-port snapshot. User and Profile must agree on tenant, subject and aggregate version. Only active users may self-service. User's lifecycle metadata never enters the public projection.

type ProfileRecoveryRequest

type ProfileRecoveryRequest struct {
	TenantID  TenantID
	CommandID CommandID
	Context   authorization.DecisionContext
}

ProfileRecoveryRequest performs current-subject scoped primary recovery only; no new fields, alternate target or mutation may be submitted.

type ProfileRepository

type ProfileRepository interface {
	GetProfile(context.Context, ProfileQuery) (ProfileRecord, error)
}

ProfileRepository owns consistent profile reads. GetProfile is bounded and concurrent-safe, returns ErrUserAbsent for absence, and never confers authority.

type ProfileService

type ProfileService struct {
	// contains filtered or unexported fields
}

ProfileService owns self-service reads, mutation and recovery without stores, workers or raw transactions. Dependencies remain caller-owned, bounded and concurrency-safe. Construction snapshots configuration without I/O.

func NewProfileService

func NewProfileService(repo ProfileRepository, uow UnitOfWork, authorize authorization.Service, redact RedactFunc, config ProfileConfig) (*ProfileService, error)

NewProfileService composes the existing field, authorization and command owners.

func (*ProfileService) ProfileGet

func (s *ProfileService) ProfileGet(ctx context.Context, req ProfileGetRequest) (out ProfileGetResult, err error)

ProfileGet returns only the exact current human subject's safe projection. Absence, blocking and output denial are indistinguishable ForbiddenError.

func (*ProfileService) ProfileUpdate

func (s *ProfileService) ProfileUpdate(ctx context.Context, req ProfileUpdateRequest) (out ProfileUpdateResult, err error)

ProfileUpdate authorizes current state and every self-writable field before a stable command. New writes must CAS the locked version and active status after command replay lookup. Its one clock sample is reused for field decisions.

func (*ProfileService) RecoverProfileUpdate

func (s *ProfileService) RecoverProfileUpdate(ctx context.Context, req ProfileRecoveryRequest) (out ProfileUpdateResult, err error)

RecoverProfileUpdate reads the original command under current subject/purpose authorization and redaction. Its result is historic, never login authority.

type ProfileUpdateCommand

type ProfileUpdateCommand struct {
	Binding          CommandBinding
	ExpectedVersion  uint64
	Name             string
	Image            string
	AdditionalFields []AttributeUpdate
}

ProfileUpdateCommand is immutable prepared intent for the existing command owner. Empty name/image are omitted; additional fields are whole replacements. New mutation requires the exact locked expected version and active status, one next version, immutable profile/result and identity.user.update audit. Replay must precede CAS. Structural construction is not authorization.

type ProfileUpdateRequest

type ProfileUpdateRequest struct {
	TenantID         TenantID
	Context          authorization.DecisionContext
	CommandID        CommandID
	SchemaVersion    uint32
	ExpectedVersion  uint64
	Name             string
	Image            string
	AdditionalFields []AttributeUpdate
	CorrelationID    string
}

ProfileUpdateRequest is the explicit-command core boundary. Empty Name/Image mean omitted, not clearing. Additional values retain false/zero/empty presence. ExpectedVersion is positive and cannot overflow its next version. Preserve CommandID and logical intent on retries; HTTP keyed/session/CSRF admission will compose this boundary and must not decode Context from a request body.

type ProfileUpdateResult

type ProfileUpdateResult struct {
	User                UserProfile
	InvalidationVersion uint64
	CommandID           CommandID
	CommitOutcome       CommitOutcome
}

ProfileUpdateResult preserves known outcome even when current disclosure fails. User/InvalidationVersion are zero when output is withheld. Invalid means no authoritative outcome observation, never inferred rollback of a prior attempt.

type ProviderRevocationOutcome

type ProviderRevocationOutcome uint8

ProviderRevocationOutcome is an initial external-intent observation. Zero is invalid. Local commit never implies successful provider network revocation.

const (
	ProviderRevocationNotRequested ProviderRevocationOutcome = iota + 1
	ProviderRevocationPending
)

type RecoveryQuery

type RecoveryQuery struct {
	CommandID CommandID
	TenantID  TenantID
	UserID    UserID
	// AnonymousFlowID selects the explicit anonymous-flow namespace, mutually
	// exclusive with UserID. Possession is never recovery authority.
	AnonymousFlowID string `json:",omitempty"`
	ActorID         ActorID
	ActorKind       authorization.SubjectKind
	OperationID     string
	RecordID        string
	LinkMode        AccountLinkMode
}

RecoveryQuery scopes a primary-authority lookup to the original command, tenant, target, actor, operation and exact record/link mode for all outcomes. The owner must match every field against immutable command scope before returning any outcome, including conflict, not-committed and in-progress. Non-record operations omit RecordID/LinkMode. A command ID alone never permits public lookup. No fields, alternate command IDs, callbacks or transaction handles are accepted.

type RedactFunc

RedactFunc is the bounded, side-effect-free redaction policy callback.

type RedactionPolicyDecision

type RedactionPolicyDecision struct {
	VisibleFields  []FieldName
	RedactedFields []FieldName
	PolicyID       string
	PolicyVersion  uint64
	Reason         PolicyReason
}

RedactionPolicyDecision describes exactly which requested fields are visible or redacted, with one registered policy ID/version. Unknown, overlapping or duplicate field names are invalid. Empty visibility omits the projection.

type RedactionPolicyInput

type RedactionPolicyInput struct {
	TenantID      TenantID
	ViewerID      ActorID
	Resource      RedactionResource
	Fields        []FieldName
	OperationID   string
	PolicyVersion uint64
	Deadline      time.Time
}

RedactionPolicyInput binds the viewer, tenant, operation, policy revision and deadline to a copied bounded projection. Callbacks are side-effect-free, concurrent-safe and must honor the supplied context; no stores/transactions.

type RedactionResource

type RedactionResource struct {
	Kind string
	ID   string
}

RedactionResource binds the exact resource kind and target being projected.

type RequestIntent

type RequestIntent struct {
	// contains filtered or unexported fields
}

RequestIntent freezes complete request input before generated values, current-state preparation or command reservation. It is correlation input, never authentication, authorization, a reservation or an execution receipt. Zero is invalid. Copies are immutable and safe for concurrent reads. Raw passwords, bearers and provider tokens are forbidden: feature owners supply purpose-bound keyed digests and map every behavior-bearing field.

func NewRequestIntent

func NewRequestIntent(fields []FeatureField) (RequestIntent, error)

NewRequestIntent validates and copies 0..128 distinct named scalars using FeatureField bounds, with at most 256 KiB total. Input order has no meaning; absent fields remain distinct from explicitly empty/false/zero values. Time preserves its microsecond-exact instant in UTC. No I/O or callbacks run. Invalid input returns redacted InvalidInputError and no usable intent. Do not mutate caller-owned input during construction. Owners still validate their native schema before admission; structural construction grants no authority.

Example
package main

import (
	"fmt"
	identity "github.com/faustbrian/go-identity"
)

func main() {
	intent, err := identity.NewRequestIntent([]identity.FeatureField{
		{Name: "remember", Value: identity.FeatureValue{Kind: identity.FeatureBool, Bool: false}},
		{Name: "expected.version", Value: identity.FeatureValue{Kind: identity.FeatureUint64, Uint64: 7}},
	})
	if err != nil {
		panic(err)
	}
	// Only the trusted adapter uses the owned projection; diagnostics stay redacted.
	fields, valid := intent.Fields()
	fmt.Println(valid, len(fields), intent)
}
Output:
true 2 identity.RequestIntent(redacted)

func (RequestIntent) Fields

func (r RequestIntent) Fields() ([]FeatureField, bool)

Fields returns an owned scalar projection only for trusted adapter canonical mapping. It returns false for zero. Mutating the result never changes intent. The projection is not a disclosure boundary: never log, serialize or return its private values through HTTP. No I/O or callbacks run.

func (RequestIntent) Format

func (RequestIntent) Format(s fmt.State, _ rune)

Format redacts every diagnostic verb, including %#v and %x.

func (RequestIntent) LogValue

func (RequestIntent) LogValue() slog.Value

LogValue emits no request fields or digests.

func (RequestIntent) MarshalJSON

func (RequestIntent) MarshalJSON() ([]byte, error)

MarshalJSON rejects implicit persistence and accidental HTTP disclosure.

func (RequestIntent) MarshalText

func (RequestIntent) MarshalText() ([]byte, error)

MarshalText rejects implicit text disclosure of private request inputs.

type RetentionAction

type RetentionAction string

RetentionAction identifies the protected privacy operation; zero is invalid.

const (
	RetentionExport    RetentionAction = "export"
	RetentionAnonymize RetentionAction = "anonymize"
	RetentionDelete    RetentionAction = "delete"
)

type RetentionDisposition

type RetentionDisposition string

RetentionDisposition never represents completed deletion or export.

const (
	RetentionAllow          RetentionDisposition = "allow"
	RetentionBlockLegalHold RetentionDisposition = "block-legal-hold"
	RetentionRetainUntil    RetentionDisposition = "retain-until"
)

type RetentionPolicyDecision

type RetentionPolicyDecision struct {
	Disposition   RetentionDisposition
	RetainUntil   time.Time
	PolicyID      string
	PolicyVersion uint64
	Reasons       []PolicyReason
}

RetentionPolicyDecision carries registered reasons and optional UTC expiry. RetainUntil requires an instant after RequestedAt; other variants omit it.

type RetentionPolicyInput

type RetentionPolicyInput struct {
	TenantID      TenantID
	ActorID       ActorID
	SubjectID     UserID
	OperationID   string
	PolicyVersion uint64
	Deadline      time.Time
	Resource      RetentionResource
	Action        RetentionAction
	LegalHold     bool
	RequestedAt   time.Time
}

RetentionPolicyInput binds a privacy decision to trusted subject/actor, action, operation and policy version. LegalHold=false is an explicit valid input; it must come from authoritative composition, not a client assertion. RequestedAt is a required trusted timestamp, normalized to UTC without sampling a clock.

type RetentionResource

type RetentionResource struct {
	Kind string
	ID   string
}

RetentionResource identifies one owned resource, with an optional bounded ID.

type RevocationOutcome

type RevocationOutcome struct {
	CascadeID           CascadeID
	Generation          uint64
	AuthorityVersion    uint64
	Status              RevocationStatus
	Owners              []RevocationOwnerReceipt
	ExternalLimitations []PolicyReason
}

RevocationOutcome is a historical cascade observation, not current authority. Admission returns generation 1, a new random ID, the incremented user authority version and Pending. Owners names every required consumer as pending, never silently omitting an unavailable consumer or asserting its acknowledgement. ExternalLimitations are registered redacted codes, never provider errors. Treat slices as immutable; public service results are caller-owned copies.

type RevocationOwnerReceipt

type RevocationOwnerReceipt struct {
	Owner  string
	Status RevocationStatus
}

RevocationOwnerReceipt names one semantic owner, not a storage implementation. Pending names a required consumer without asserting its acknowledgement.

type RevocationStatus

type RevocationStatus uint8

RevocationStatus separates pending cleanup from closed or unknown cleanup. Its zero value is invalid. A committed owner admission is always pending.

const (
	RevocationPending RevocationStatus = iota + 1
	RevocationCompleted
	RevocationUnknown
)

type RiskAction

type RiskAction string

RiskAction is a policy decision, not an authentication or consumption proof.

const (
	RiskAllow  RiskAction = "allow"
	RiskDeny   RiskAction = "deny"
	RiskStepUp RiskAction = "step-up"
)

type RiskLevel

type RiskLevel string

RiskLevel classifies risk; the empty value is invalid, never implicitly low.

const (
	RiskLow    RiskLevel = "low"
	RiskMedium RiskLevel = "medium"
	RiskHigh   RiskLevel = "high"
)

type RiskPolicyDecision

type RiskPolicyDecision struct {
	Level         RiskLevel
	Action        RiskAction
	PolicyID      string
	PolicyVersion uint64
	Reasons       []PolicyReason
}

RiskPolicyDecision has closed enums and 1..32 registered unique reasons. Deny and StepUp are valid decisions but never permission to perform a mutation.

type RiskPolicyInput

type RiskPolicyInput struct {
	TenantID      TenantID
	ActorID       ActorID
	SubjectID     UserID
	OperationID   string
	PolicyVersion uint64
	Deadline      time.Time
	Signals       []RiskSignal
}

RiskPolicyInput binds a pure risk decision to its exact operation and actor. SubjectID may be absent before registration; Signals must be explicit (a nonnil empty slice is valid). Deadline is narrowed to the bounded context.

type RiskSecurityDecision

type RiskSecurityDecision string

RiskSecurityDecision is the closed risk classification, not authentication, revocation or a database/audit commit outcome. Zero is invalid.

const (
	RiskSecurityAllow    RiskSecurityDecision = "allow"
	RiskSecurityDeny     RiskSecurityDecision = "deny"
	RiskSecurityThrottle RiskSecurityDecision = "throttle"
	RiskSecurityStepUp   RiskSecurityDecision = "step_up"
)

type RiskSecurityEventInput

type RiskSecurityEventInput struct {
	Decision                              RiskSecurityDecision
	OccurredAt                            time.Time
	TenantID                              TenantID
	CommandID, CorrelationID, CausationID CommandID
	Actor                                 audit.ActorInput
	Subject                               SecuritySubject
	SubjectID, PolicyID                   string
	PolicyVersion                         uint64
	ImpersonationGrantID                  string
}

RiskSecurityEventInput is trusted risk-owner attribution for a non-mutating decision. Subject is a real authentication_attempt or known user; syntax does not establish provenance. ImpersonationGrantID is required exactly when the effective Actor has one DelegatedBy. Command/correlation identify the owning evaluation; optional causation retains an actual prior command or audit event. Caller owns attribution until BuildRiskDecision returns and must not mutate it concurrently. No provider facts, network, raw identifier, arbitrary reason, counter state or bearer can be attached to this input.

type RiskSignal

type RiskSignal struct {
	Name       string
	Value      AttributeScalar
	ObservedAt time.Time
}

RiskSignal is one bounded typed observation. Values must not contain secrets. Strings are at most 512 bytes; ObservedAt is required and normalized to UTC.

type SecurityAction

type SecurityAction string

SecurityAction is a closed implemented subset of the security taxonomy. Further package actions require their owned field contract before support.

const (
	SecurityHIBPQueryRange  SecurityAction = "identity.hibp.query_range"
	SecurityHIBPDetectMatch SecurityAction = "identity.hibp.detect_match"
	SecurityHIBPRejectRange SecurityAction = "identity.hibp.reject_range"
)

type SecurityEventInput

type SecurityEventInput struct {
	Action                                SecurityAction
	OccurredAt                            time.Time
	Outcome                               audit.Outcome
	Reason                                SecurityReason
	TenantID                              TenantID
	CommandID, CorrelationID, CausationID CommandID
	Actor                                 audit.ActorInput
	Subject                               SecuritySubject
	SubjectID, PolicyID                   string
	PolicyVersion                         uint64
	Provider                              SecurityProvider
}

SecurityEventInput is trusted in-process attribution, not transport input. Its fields cannot contain arbitrary attributes, descriptions, network data, hashes, breach counts, raw claims or provider diagnostics. IDs must be stable canonical internal IDs, never credentials or raw user identifiers. Actor delegation is copied synchronously; callers must not mutate it during Build. Initial support is the HIBP action contract with a real authentication attempt subject and protected retention. Other action contracts fail closed. Command and correlation use command IDs. Causation additionally admits a canonical existing audit event ID carried unchanged in the named string type.

type SecurityProvider

type SecurityProvider struct {
	Kind    SecurityProviderKind
	Profile SecurityProviderProfile
	Version uint64
	Result  SecurityProviderResult
}

SecurityProvider contains only registered safe provider attribution. Version is positive and encoded once as canonical decimal at the audit boundary.

type SecurityProviderKind

type SecurityProviderKind string

SecurityProviderKind identifies an implemented provider without endpoint data.

const SecurityProviderHIBP SecurityProviderKind = "hibp"

type SecurityProviderProfile

type SecurityProviderProfile string

SecurityProviderProfile is a registered immutable protocol/privacy profile.

const SecurityHIBPRange SecurityProviderProfile = "hibp.range.padded.v1"
const SecurityHIBPRangeUnpadded SecurityProviderProfile = "hibp.range.unpadded.v1"

SecurityHIBPRangeUnpadded identifies explicit range checking without response padding. It must never attest the recommended padded privacy profile.

type SecurityProviderResult

type SecurityProviderResult string

SecurityProviderResult describes evidence, not password acceptance or storage.

const (
	SecurityProviderAccepted          SecurityProviderResult = "accepted"
	SecurityProviderResultDenied      SecurityProviderResult = "denied"
	SecurityProviderResultUnavailable SecurityProviderResult = "unavailable"
	SecurityProviderResultTimeout     SecurityProviderResult = "timeout"
	SecurityProviderThrottled         SecurityProviderResult = "throttled"
	SecurityProviderMalformed         SecurityProviderResult = "malformed"
	SecurityProviderAmbiguous         SecurityProviderResult = "ambiguous"
	SecurityProviderBindingMismatch   SecurityProviderResult = "binding_mismatch"
	SecurityProviderReplayed          SecurityProviderResult = "replayed"
)

type SecurityReason

type SecurityReason string

SecurityReason is the bounded shared reason catalog, never free text.

const (
	SecurityCompleted           SecurityReason = "completed"
	SecurityInvalidInput        SecurityReason = "invalid_input"
	SecurityNotAuthenticated    SecurityReason = "not_authenticated"
	SecurityNotAuthorized       SecurityReason = "not_authorized"
	SecurityNotFoundHidden      SecurityReason = "not_found_hidden"
	SecurityConflict            SecurityReason = "conflict"
	SecurityExpired             SecurityReason = "expired"
	SecurityReplayed            SecurityReason = "replayed"
	SecurityRateLimited         SecurityReason = "rate_limited"
	SecurityRiskDenied          SecurityReason = "risk_denied"
	SecurityStepUpRequired      SecurityReason = "step_up_required"
	SecurityProviderDenied      SecurityReason = "provider_denied"
	SecurityProviderUnavailable SecurityReason = "provider_unavailable"
	SecurityTimeout             SecurityReason = "timeout"
	SecurityCancelled           SecurityReason = "cancelled"
	SecurityCommitUnknown       SecurityReason = "commit_unknown"
)

type SecuritySubject

type SecuritySubject string

SecuritySubject is a closed safe resource classification. Initial HIBP construction uses a real pre-auth transaction or authenticated issuer challenge, never an identifier, digest or fabricated attempt.

const SecurityAuthenticationAttempt SecuritySubject = "authentication_attempt"
const SecurityUser SecuritySubject = "user"

SecurityUser identifies a known internal user, never an email, phone number or provider subject. Imported canonical IDs remain unchanged.

type Sensitivity

type Sensitivity string

Sensitivity is the registered data classification. Secret fields cannot be indexed or made output-readable. These values must never hold credentials.

const (
	SensitivityPublic    Sensitivity = "public"
	SensitivityPersonal  Sensitivity = "personal"
	SensitivitySensitive Sensitivity = "sensitive"
	SensitivitySecret    Sensitivity = "secret"
)

type Status

type Status string

Status is the closed persisted identity lifecycle state. Its zero value and unknown values are invalid. Status is not a pending cascade/job state.

const (
	// StatusActive is eligible for authentication, subject to other authorities.
	StatusActive Status = "active"
	// StatusSuspended denies authentication until an explicit restore command.
	StatusSuspended Status = "suspended"
	// StatusBanned denies authentication until an explicit authorized unban.
	StatusBanned Status = "banned"
	// StatusAnonymized is terminal for authentication and personal identity.
	StatusAnonymized Status = "anonymized"
	// StatusDeleted is terminal and must never be restored by import or policy.
	StatusDeleted Status = "deleted"
)

func (Status) AllowsAuthentication

func (s Status) AllowsAuthentication() bool

AllowsAuthentication reports only lifecycle eligibility, not authentication or authorization. Unknown, blocked and terminal states fail closed. Elapsed ban/suspension time does not implicitly restore state or old credentials.

func (Status) Valid

func (s Status) Valid() bool

Valid reports whether s is a registered lifecycle state.

type StatusPolicy

type StatusPolicy struct {
	AllowedTransitions      []StatusTransition
	MaximumSuspension       time.Duration
	IdentifierReuseCooldown time.Duration
	BanRequiresReason       bool
}

StatusPolicy describes allowed lifecycle transitions and retention bounds. Configure it before use and do not mutate it or its slice during concurrent reads. Consumers retaining configuration must own a deep copy. A false reason requirement and zero reuse cooldown are explicit valid choices. Zero policy is invalid; no implicit default edges or duration limits are applied.

func (StatusPolicy) CheckTransition

func (p StatusPolicy) CheckTransition(from, to Status, hasReason bool, restriction time.Duration) error

CheckTransition checks one proposed edge without changing a user or granting authority. hasReason must come from the validated command. A zero restriction duration means indefinite suspension/ban; positive duration is allowed only for those target states and never exceeds MaximumSuspension. The command owner must separately authorize, version-fence, persist and acknowledge all required cascades before a terminal status is reported complete.

Example
package main

import (
	"fmt"
	"time"

	"github.com/faustbrian/go-identity"
)

func main() {
	policy := identity.StatusPolicy{
		AllowedTransitions: []identity.StatusTransition{
			{From: identity.StatusActive, To: identity.StatusSuspended, ReasonRequired: true},
		},
		MaximumSuspension: 24 * time.Hour,
	}
	// This checks a proposed edge, not caller authorization or committed state.
	fmt.Println(policy.CheckTransition(identity.StatusActive, identity.StatusSuspended, true, time.Hour))
	fmt.Println(policy.CheckTransition(identity.StatusActive, identity.StatusSuspended, false, time.Hour))
}
Output:
<nil>
identity: invalid input

func (StatusPolicy) Validate

func (p StatusPolicy) Validate() error

Validate checks all configuration before any transition decision. There must be 1..32 distinct edges, a positive suspension maximum up to 87,600 hours and a nonnegative reuse cooldown up to the same bound. Self edges, unknown states, deleted outgoing edges and anonymized edges other than deletion are invalid.

type StatusTransition

type StatusTransition struct {
	From           Status
	To             Status
	ReasonRequired bool
}

StatusTransition permits one explicit directed edge. ReasonRequired applies to a validated command reason, not to authority or proof supplied by a caller.

type SubjectID

type SubjectID string

SubjectID is the canonical 22-character unpadded base64url encoding of a non-zero 128-bit subject identifier. The zero value is invalid. Values are immutable and safe for concurrent reads; reconstruct wire values with ParseSubjectID. A SubjectID alone proves neither identity nor tenant authority.

func NewSubjectID

func NewSubjectID() (SubjectID, error)

NewSubjectID reads exactly 16 bytes from crypto/rand, rejects all-zero entropy and returns its canonical representation. Failure returns an empty ID and ErrSubjectIDEntropy. The function retains no caller state or resources.

func ParseSubjectID

func ParseSubjectID(raw string) (SubjectID, error)

ParseSubjectID accepts only a canonical unpadded base64url encoding of 16 non-zero bytes. It checks the length before decoding; invalid input returns an empty ID and ErrInvalidSubjectID without disclosing the input.

Example
package main

import (
	"fmt"

	"github.com/faustbrian/go-identity"
)

func main() {
	id, err := identity.ParseSubjectID("AAAAAAAAAAAAAAAAAAAAAQ")
	if err != nil {
		return
	}
	fmt.Println(identity.FormatSubjectID(id))
}
Output:
AAAAAAAAAAAAAAAAAAAAAQ

type TenantID

type TenantID string

TenantID is a case-sensitive tenant identity of at most 128 UTF-8 bytes. The empty value is invalid. A tenant ID is not proof of tenant access.

type TypedClaim

type TypedClaim struct {
	Name        string
	Value       AttributeScalar
	Sensitivity Sensitivity
}

TypedClaim is a lossless scalar with explicit sensitivity. Secret values are forbidden in mapped claims; credentials are never policy inputs or outputs.

type UnauthenticatedError

type UnauthenticatedError struct{}

UnauthenticatedError is missing human identity; it retains no caller data.

func (UnauthenticatedError) Error

func (UnauthenticatedError) Error() string

type UnavailableError

type UnavailableError struct{}

UnavailableError is a redacted dependency, panic or invalid-output failure.

func (UnavailableError) Error

func (UnavailableError) Error() string

type UnitOfWork

type UnitOfWork interface {
	Execute(context.Context, Command) (CommandResult, error)
	Recover(context.Context, RecoveryQuery) (CommandResult, error)
}

UnitOfWork is the storage-neutral durable command owner. Execute must enforce a global command-ID claim, exact tenant/actor/purpose/canonical-input binding, idempotency, user uniqueness including tombstones, and one atomic user/result/ required audit commit. Recover queries the primary, never a projection/cache. Implementations are concurrent-safe and bounded by ctx. They cannot infer NotCommitted from cancellation or return Committed without required effects. A PostgreSQL adapter alone maps typed commands to its concrete coordinator; the core exposes no participant registration, work handle or transaction.

type UnknownCommitError

type UnknownCommitError struct{ CommandID CommandID }

UnknownCommitError requires scoped recovery of the same command, not a new ID.

func (UnknownCommitError) Error

func (UnknownCommitError) Error() string

type UpdateUserCommand

type UpdateUserCommand struct {
	Binding         CommandBinding
	ExpectedVersion uint64
	Attributes      []AttributeUpdate
}

UpdateUserCommand is a whole-value typed-field update, never a status, credential, identifier or role transition. ExpectedVersion is positive and below uint64's maximum. The durable owner checks replay before fencing the locked current version/status, then atomically persists fields, next aggregate version, original command result and required identity.user.update audit. Deleted/anonymized users cannot be updated or resurrected. Structural command construction alone grants no authorization or schema-validation authority.

type User

type User struct {
	ID       UserID
	TenantID TenantID
	Status   Status
	Version  uint64
}

User is a versioned directory projection. Repository/UnitOfWork owns writes; callers treat returned values as immutable snapshots. Validation grants no access, changes no state and does not confirm lifecycle cascade completion.

func (User) Validate

func (u User) Validate(tenant TenantID) error

Validate checks a user projection against the exact expected tenant. It rejects malformed/oversized IDs, unknown statuses and zero versions without rewriting identifiers. It is structural validation only; canonical-ID policy, authoritative lookup, freshness and caller authorization remain separate.

Example
package main

import (
	"fmt"

	"github.com/faustbrian/go-identity"
)

func main() {
	user := identity.User{
		ID: "01ARZ3NDEKTSV4RRFFQ69G5FAV", TenantID: "tenant-a",
		Status: identity.StatusActive, Version: 1,
	}
	fmt.Println(user.Validate("tenant-a"))
	fmt.Println(user.Validate("tenant-b"))
}
Output:
<nil>
identity: invalid input

type UserCreateRecoveryRequest

type UserCreateRecoveryRequest struct {
	CommandID  CommandID
	TenantID   TenantID
	UserID     UserID
	ActorScope authorization.DecisionContext
}

UserCreateRecoveryRequest cannot replace inputs or allocate a new command. ActorScope authorizes current creation-result access for the exact target.

type UserCreateRequest

type UserCreateRequest struct {
	CommandID     CommandID
	TenantID      TenantID
	UserID        UserID
	SchemaVersion uint32
	Fields        []AttributeUpdate
	ActorScope    authorization.DecisionContext
	CorrelationID string
}

UserCreateRequest is the explicit-ID core command boundary. Trusted admission preallocates a random CommandID and stable canonical UserID; every retry keeps them and the same logical fields. HTTP keyed admission is a separate owned transport/adapter concern, not caller-written workflow glue. Fields are at most 64 explicit values; defaults may expand the prepared command to 128. ActorScope comes from trusted authentication composition, never client JSON.

type UserCreateResult

type UserCreateResult struct {
	CommandID     CommandID
	User          User
	CommitOutcome CommitOutcome
}

UserCreateResult preserves durable outcome even when output is withheld by cancellation or current redaction. User is disclosed only on permitted success. A committed result is an initial historical snapshot, not current login state. CommitOutcomeInvalid means this call made no authoritative outcome observation; it never implies that an earlier attempt with this command ID rolled back.

type UserCreationConfig

type UserCreationConfig struct {
	Query         UserQueryConfig
	Schema        AttributeSchema
	Normalize     NormalizeAttributeFunc
	ValidateValue ValidateAttributeFunc
}

UserCreationConfig is explicit immutable composition. Query supplies shared current authorization/redaction/time/ID policy. The schema, normalizer and validator are the same field-enforcement implementation used by typed writes.

type UserCreationService

type UserCreationService struct {
	// contains filtered or unexported fields
}

UserCreationService owns authorized creation and recovery orchestration. It has no stores, goroutines, locks, arbitrary participant callbacks or commits. The full identity Service will compose this same implementation and reads. Dependencies remain caller-owned and must be concurrent-safe/context-bounded.

func NewUserCreationService

func NewUserCreationService(uow UnitOfWork, authorize authorization.Service, redact RedactFunc, config UserCreationConfig) (*UserCreationService, error)

NewUserCreationService validates and freezes composition without I/O.

func (*UserCreationService) RecoverUserCreate

func (s *UserCreationService) RecoverUserCreate(ctx context.Context, req UserCreateRecoveryRequest) (result UserCreateResult, err error)

RecoverUserCreate authorizes current result access before a scoped primary lookup. It does not execute, rewrite fields, consume proofs or issue new IDs. Historic policy/schema bindings remain intact while current policy controls disclosure. Committed recovery does not assert current user status/freshness.

func (*UserCreationService) UserCreate

func (s *UserCreationService) UserCreate(ctx context.Context, req UserCreateRequest) (result UserCreateResult, err error)

UserCreate admits/normalizes/authorizes before the first durable attempt. After Execute begins, malformed output, dependency failure or panic is Unknown unless a valid authoritative receipt proves another exact outcome. Required audit and logical command replay belong to that same adapter commit. Current redaction is reapplied after every committed replay; no stale grants survive.

type UserDirectoryRepository

type UserDirectoryRepository interface {
	UserQueryRepository
	UserListRepository
}

UserDirectoryRepository composes reads and listing without a parallel store.

type UserGetRequest

type UserGetRequest struct {
	TenantID TenantID
	UserID   UserID
	Context  authorization.DecisionContext
}

UserGetRequest binds a target to an exact upstream authorization context. Trusted authenticated composition supplies Context; it must never be decoded from a client override. IDs and this request alone confer no authority.

type UserGetResult

type UserGetResult struct{ User User }

UserGetResult is a permission-filtered directory snapshot, not a credential.

type UserID

type UserID string

UserID identifies a directory user without encoding tenant or authority. It preserves an existing canonical ID, including a ULID, unchanged. Structural validation is not a substitute for the composition's canonical-ID policy. The empty value is invalid; IDs are case-sensitive and at most 128 bytes.

type UserListConfig

type UserListConfig struct {
	CursorKey                   [32]byte
	CursorTTL                   time.Duration
	ValidateCanonicalIdentifier func(context.Context, IdentifierKind, string) error
}

UserListConfig is immutable explicit cursor composition. CursorKey must be an independently generated 256-bit secret (all-zero is invalid). It must not be logged; rotation invalidates old cursors. TTL is positive, at most 15m. The canonical identifier validator is bounded, concurrency-safe and must validate without rewriting indexed values. Cursor possession grants no access.

type UserListFilters

type UserListFilters struct {
	Statuses            []Status
	IdentifierKind      IdentifierKind
	CanonicalIdentifier string
}

UserListFilters contains exact indexed filters; at least one known status is required, at most 16, without duplicates. Identifier kind/value are paired. CanonicalIdentifier is at most 512 UTF-8 bytes, never free-text search.

type UserListRepository

type UserListRepository interface {
	List(context.Context, ListUsersQuery) (UserPage, error)
}

UserListRepository owns authoritative snapshot selection and retention. Implementations must be concurrency-safe and obey ListUsersQuery semantics.

type UserListRequest

type UserListRequest struct {
	TenantID TenantID
	Filters  UserListFilters
	Cursor   string
	Limit    uint32
	Context  authorization.DecisionContext
}

UserListRequest binds a collection to a trusted authenticated upstream context. Resource type is identity.user.collection, ID is the exact tenant, and action is identity.user.list. The service overwrites reserved query attributes with the actual filters and limit, preventing dishonest bindings.

type UserListResult

type UserListResult struct {
	Users      []User
	NextCursor string
}

UserListResult contains only authorized/redacted users. NextCursor advances over all scanned rows, including omissions, without exposing hidden IDs.

type UserPage

type UserPage struct {
	Users []User
	Next  Cursor
}

UserPage is a caller-owned bounded ordered snapshot page. Next is absent only at the end; otherwise its position advances exactly by len(Users). An empty page is terminal. No totals or storage diagnostics are disclosed.

type UserProfile

type UserProfile struct {
	ID               UserID
	TenantID         TenantID
	Name             string
	Image            string
	Version          uint64
	AdditionalFields []AttributeUpdate
}

UserProfile is the self-service projection. It deliberately has no email, status, roles, credentials or authentication authority. AdditionalFields are private copies filtered by schema output permission and current redaction. Version is the positive identity aggregate version, not session freshness. Formatting is redacted; explicit response serialization is caller-owned.

func (UserProfile) Format

func (UserProfile) Format(s fmt.State, verb rune)

Format prevents implicit display of personal profile contents.

type UserQueryConfig

type UserQueryConfig struct {
	Clock                   clock.Clock
	MaximumDuration         time.Duration
	PolicyID                string
	PolicyVersion           uint64
	RedactionReasons        []PolicyReason
	ValidateCanonicalUserID CanonicalUserIDValidator
}

UserQueryConfig is explicit immutable read composition. Clock is sampled once per call at authorization; MaximumDuration is positive and at most one minute. PolicyID/version bind both authorization and redaction. All callbacks and dependencies must be bounded and concurrency-safe; no implicit defaults. RedactionReasons is copied at construction and cannot be empty.

type UserQueryRepository

type UserQueryRepository interface {
	Get(context.Context, GetUserQuery) (User, error)
}

UserQueryRepository owns authoritative user reads. Implementations must be concurrent-safe, honor cancellation/deadlines, and return ErrUserAbsent for absence without leaking personal data. The full Repository will compose this narrow read seam with its write/list contracts; this is not a second store.

type UserQueryService

type UserQueryService struct {
	// contains filtered or unexported fields
}

UserQueryService is the shared user-read implementation. It owns no store, goroutines or locks and grants no caller identity. Its constructor permits an explicit read-only composition without mutation/capability dependencies; the full identity service composes this same query implementation.

func NewUserDirectoryService

func NewUserDirectoryService(repo UserDirectoryRepository, authorize authorization.Service, redact RedactFunc, config UserQueryConfig, listConfig UserListConfig) (*UserQueryService, error)

NewUserDirectoryService composes the existing get implementation with listing; NewUserQueryService remains get-only. No repository or policy I/O is performed during construction and all configuration is copied.

func NewUserQueryService

func NewUserQueryService(repo UserQueryRepository, authorize authorization.Service, redact RedactFunc, config UserQueryConfig) (*UserQueryService, error)

NewUserQueryService validates and snapshots the read composition without I/O. It rejects nil/typed-nil dependencies, missing policies and unbounded config.

func (*UserQueryService) UserGet

func (s *UserQueryService) UserGet(ctx context.Context, req UserGetRequest) (result UserGetResult, err error)

UserGet admits scope before policy or storage work, requires explicit Allow at the selected policy revision, checks authoritative output and applies redaction before disclosure. No provider hints/role strings grant authority. Repository absence is indistinguishable from forbidden output. Cancellation, callback panic/error and malformed outputs fail closed with no user returned.

func (*UserQueryService) UserList

func (s *UserQueryService) UserList(ctx context.Context, req UserListRequest) (result UserListResult, err error)

UserList authorizes before reading and reauthorizes every snapshot row before applying redaction. Every page uses one clock sample and one bounded context. Denied rows are omitted; dependency failures, panics, malformed pages and cancellation return no partial result. A cursor cannot renew its original expiry, change its query/viewer/tenant/policy, or authorize a later page.

type UserStatusCommand

type UserStatusCommand struct {
	Binding                  CommandBinding
	ExpectedVersion          uint64
	ExpectedAuthorityVersion uint64
	StatusPolicyVersion      uint64
	TargetStatus             Status
	Reason                   string
	ExpiresAt                time.Time
}

UserStatusCommand is immutable lifecycle intent. Both expected versions and absolute expiry are stable across retries; replay is checked before locked CAS. The owner fences both versions, checks the configured policy version and transition, bumps both counters once, and atomically stores status, expiry, audit, lifecycle event/outbox, immutable required-consumer snapshot and receipt. Suspend/ban emit identity.suspended.v1 and audit identity.user.suspend; restore/unban emit identity.restored.v1 and audit identity.user.restore. Restore is only from suspended; unban only from banned. Neither revives any pre-transition credential. Expiry requires an audited fresh restore command, never passive eligibility. Consumer closure is NOT part of admission.

func (UserStatusCommand) RequiredConsumers

func (s UserStatusCommand) RequiredConsumers() []string

RequiredConsumers returns a caller-owned ordered copy of the exact immutable identity-lifecycle-consumers-v1 manifest for this operation. Adapters snapshot every ID and its public-contract fingerprint at admission; missing consumers cannot be omitted merely because they are not running. No acknowledgement is granted by this list or command construction.

type UserStatusConfig

type UserStatusConfig struct {
	Query         UserQueryConfig
	Policy        StatusPolicy
	PolicyVersion uint64
	Reasons       []string
}

UserStatusConfig is immutable explicit lifecycle composition. Reasons contains 1..32 unique registered UTF-8 codes, each at most 512 bytes without whitespace or controls. PolicyVersion identifies the frozen transition policy independently of authorization revision. No implicit expiry or transition defaults exist.

type UserStatusQuery

type UserStatusQuery struct {
	TenantID TenantID
	UserID   UserID
}

UserStatusQuery is an exact partitioned primary lookup, never a cache read.

type UserStatusRecord

type UserStatusRecord struct {
	User             User
	AuthorityVersion uint64
}

UserStatusRecord is one consistent primary snapshot. AuthorityVersion is the user security dimension, not User.Version: profile writes may increment the latter alone. Every authority change must also advance User.Version.

type UserStatusRecoveryRequest

type UserStatusRecoveryRequest struct {
	CommandID  CommandID
	TenantID   TenantID
	UserID     UserID
	ActorScope authorization.DecisionContext
}

UserStatusRecoveryRequest looks up one original command under current policy. ActorScope.Action must name the original literal operation, not a new purpose.

type UserStatusRepository

type UserStatusRepository interface {
	GetStatus(context.Context, UserStatusQuery) (UserStatusRecord, error)
}

UserStatusRepository owns consistent bounded primary lifecycle reads. Absence returns ErrUserAbsent. The same durable identity owner implements UnitOfWork.

type UserStatusRequest

type UserStatusRequest struct {
	CommandID                CommandID
	TenantID                 TenantID
	UserID                   UserID
	ExpectedVersion          uint64
	ExpectedAuthorityVersion uint64
	Reason                   string
	ExpiresAt                time.Time
	ActorScope               authorization.DecisionContext
	CorrelationID            string
}

UserStatusRequest is the explicit-ID core mutation boundary. ActorScope comes from trusted authenticated composition, never client JSON. Retain both expected versions, command ID, reason and absolute expiry on retries. Suspend/ban require a registered reason; restore/unban reject reason/expiry. Zero expiry means indefinite. Transport owns session freshness, CSRF and keyed HTTP admission.

type UserStatusResult

type UserStatusResult struct {
	CommandID     CommandID
	CommitOutcome CommitOutcome
	User          User
	Revocation    RevocationOutcome
}

UserStatusResult separates authoritative command outcome from disclosure. Withheld output leaves User/Revocation zero without erasing known commitment.

type UserStatusService

type UserStatusService struct {
	// contains filtered or unexported fields
}

UserStatusService owns policy and commands, not persistence, cleanup workers, transactions or goroutines. Dependencies/callbacks must be concurrency-safe.

func NewUserStatusService

func NewUserStatusService(repo UserStatusRepository, uow UnitOfWork, authorize authorization.Service, redact RedactFunc, config UserStatusConfig) (*UserStatusService, error)

NewUserStatusService validates and copies configuration without I/O.

func (*UserStatusService) AdminUserBan

AdminUserBan requires literal identity.admin.user-ban authorization, not a role label. It bumps authority and admits pending cleanup exactly once.

func (*UserStatusService) AdminUserUnban

AdminUserUnban restores only banned eligibility, not old credential authority.

func (*UserStatusService) RecoverUserStatus

func (s *UserStatusService) RecoverUserStatus(ctx context.Context, req UserStatusRecoveryRequest) (result UserStatusResult, err error)

RecoverUserStatus performs a primary, exact-scope receipt lookup under current authorization, never Execute. The historical status is not current authority.

func (*UserStatusService) UserRestore

UserRestore restores only suspended eligibility with a fresh authority version; previously issued credentials never become usable again.

func (*UserStatusService) UserSuspend

UserSuspend denies new authority and admits pending cleanup under explicit identity.user.suspend authorization. Commitment does not certify closure.

type UserUpdateAdminRecoveryRequest

type UserUpdateAdminRecoveryRequest struct {
	CommandID  CommandID
	TenantID   TenantID
	UserID     UserID
	ActorScope authorization.DecisionContext
}

UserUpdateAdminRecoveryRequest authorizes exact scoped historical result access without new fields, command IDs or a second mutation. It is not current state.

type UserUpdateAdminRequest

type UserUpdateAdminRequest struct {
	CommandID       CommandID
	TenantID        TenantID
	UserID          UserID
	ExpectedVersion uint64
	SchemaVersion   uint32
	Fields          []AttributeUpdate
	ActorScope      authorization.DecisionContext
	CorrelationID   string
}

UserUpdateAdminRequest is the explicit-ID core administrative update boundary. Trusted composition supplies the exact upstream ActorScope; role labels grant nothing. Retain command ID, target, expected version and logical fields on retries. Field schema/write rules apply to 1..64 updates without defaults. Keyed HTTP admission remains the platform transport/adapter's pending work.

type UserUpdateAdminResult

type UserUpdateAdminResult struct {
	CommandID     CommandID
	User          User
	Version       uint64
	CommitOutcome CommitOutcome
}

UserUpdateAdminResult separates a durable outcome from current disclosure. Version/User are zero on withheld output. Invalid means no authoritative command-outcome observation, never evidence that an earlier attempt rolled back.

type UserUpdateAdminService

type UserUpdateAdminService struct {
	// contains filtered or unexported fields
}

UserUpdateAdminService owns versioned field-update admission. It owns no stores, locks, goroutines or transactions. Dependencies must be bounded and concurrency-safe. Current row status/version is trusted only after exact authorization and scoped authoritative read; the adapter still fences races. Core/read/field/outcome enforcement reuses the existing implementations.

func NewUserUpdateAdminService

func NewUserUpdateAdminService(repo UserQueryRepository, uow UnitOfWork, authorize authorization.Service, redact RedactFunc, config UserCreationConfig) (*UserUpdateAdminService, error)

NewUserUpdateAdminService freezes existing field and policy composition.

func (*UserUpdateAdminService) RecoverUserUpdateAdmin

RecoverUserUpdateAdmin is primary scoped recovery with current authorization and redaction; it never runs Execute or treats historical state as login state.

func (*UserUpdateAdminService) UserUpdateAdmin

func (s *UserUpdateAdminService) UserUpdateAdmin(ctx context.Context, req UserUpdateAdminRequest) (result UserUpdateAdminResult, err error)

UserUpdateAdmin requires current operation and per-field Allow. A read-admission decision precedes state access, then apply decisions see the trusted row's status/version. The two phases replace caller metadata; neither implies an administrative role. The single clock sample is reused across both phases. Execute owns stale-version conflict versus same-command replay. Future expected versions are rejected before execution against older policy state.

type ValidateAttributeFunc

type ValidateAttributeFunc func(context.Context, FieldPolicy, AttributeValue) error

ValidateAttributeFunc applies the composition's versioned field constraints after normalization and intrinsic bounds. Inputs are private copies; mutation cannot change prepared values or retained defaults. Implementations must be side-effect-free, bounded, concurrent-safe and contain no stores/workflows.

type Verification

type Verification struct {
	IdentifierID IdentifierID
	State        VerificationState
	VerifiedAt   time.Time
	Version      uint64
}

Verification records the identity-owned verification state of an exact IdentifierID. Version is independent of the identifier record's version; primary selection does not mint verification or reset its original time. VerifiedAt is absent for pending and may retain a historical time for revoked.

func (Verification) Format

func (Verification) Format(s fmt.State, verb rune)

Format redacts verification metadata in diagnostic output.

func (Verification) Validate

func (v Verification) Validate(identifier Identifier, now time.Time) error

Validate checks one identifier's consistent verification projection and one trusted current time. Persisted instants have microsecond precision; verified time is nonzero, no later than now and within years 1..9999. Nothing is fetched or consumed. In particular setting Verified does not prove a challenge.

type VerificationState

type VerificationState uint8

VerificationState is closed persisted verification state, not a challenge or bearer. Pending requires a fresh feature-owned challenge; revoked is denied.

const (
	VerificationPending VerificationState = iota + 1
	VerificationVerified
	VerificationRevoked
)

Directories

Path Synopsis
anonymous module
postgres module
delivery module
postgres module
email module
http module
anonymous module
email module
magiclink module
mfa module
otp module
passkey module
password module
phone module
session module
i18n module
internal
velocity
Package velocity carries private, immutable custody between the owned journal, native projection and reference composition.
Package velocity carries private, immutable custody between the owned journal, native projection and reference composition.
magiclink module
postgres module
mfa module
postgres module
otp module
postgres module
password module
postgres module
phone module
postgres module
reference module
risk module
captcha module
hibp module
postgres module
valkey module
session module
postgres module
username module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL