Documentation
¶
Overview ¶
Package accesscore implements the accesscore Cell: identity management, session lifecycle (login/refresh/logout/validate), RBAC authorization, and role queries.
cell_providers.go hosts AccessCore's "exposed service" provider methods — accessors that other layers (runtime/auth middleware) consume to wire cross-cutting concerns. Routing, event subscription, health probe, and lifecycle wiring now live in cell_init.go (Batch 3 Registry migration). Constructor + options live in cell.go; Init() + slice construction in cell_init.go.
Index ¶
- Constants
- func DefaultRefreshPolicy() refresh.Policy
- func RegisterReadiness(reg cell.Registrar, p healthz.RepoProber) error
- type AccessCore
- func (c *AccessCore) Authorizer() auth.Authorizer
- func (c *AccessCore) Init(ctx context.Context, reg cell.Registrar) error
- func (c *AccessCore) RecordBootstrapAuthFail(ctx context.Context, reason string, clientIPHash redaction.IPHash) error
- func (c *AccessCore) TokenVerifier() kauth.IntentTokenVerifier
- type NoopSetupLock
- type Option
- func WithBootstrapAuth(mw func(http.Handler) http.Handler) Option
- func WithCASProtocol(p *cas.Protocol) Option
- func WithConfigEventCollector(collector obmetrics.ConfigEventCollector) Option
- func WithConfigGetter(c ports.ConfigGetter) Option
- func WithCursorCodec(codec *query.CursorCodec) Option
- func WithEmitter(e outbox.CellEmitter) Option
- func WithJWTIssuer(issuer *auth.JWTIssuer) Option
- func WithJWTVerifier(verifier *auth.JWTVerifier) Option
- func WithLockoutMetrics(rec accountlockout.MetricsRecorder) Option
- func WithLogger(l *slog.Logger) Option
- func WithMemBundle(b accessmem.Bundle) Option
- func WithMetricsProvider(p metrics.Provider) Option
- func WithOutboxDeps(pub outbox.CellPublisher, writer outbox.CellWriter) Option
- func WithPGBundle(b accesspg.Bundle) Option
- func WithPasswordHasher(h credential.Hasher) Option
- func WithRefreshGC(interval, retention time.Duration) Option
- func WithRefreshStore(store refresh.Store) Option
- func WithSessionStore(s session.Store) Option
Constants ¶
const ( // DefaultRefreshReuseInterval is the maximum window during which a parent // refresh token may be re-presented after rotation before triggering reuse // detection. 2s mirrors the original WithInMemoryDefaults value and is // short enough that legitimate client retries fit while attackers replaying // outside the window get caught. DefaultRefreshReuseInterval = 2 * time.Second // DefaultRefreshMaxAge is the absolute lifetime of a refresh chain root. // 7 days matches the long-lived refresh token expectation set by S2/S3. DefaultRefreshMaxAge = 7 * 24 * time.Hour )
Refresh policy defaults for accesscore — the canonical numbers used by both the mem refresh store (composition root demo path + tests) and the PG refresh store. Centralizing the literals here keeps a single source of truth for ReuseInterval / MaxAge / MaxIdle / GraceMaxReuses across backends and satisfies PROD-DURATION-CONST-01 / TEST-TIME-LITERAL-01.
const PasswordVersionField = "password_version"
PasswordVersionField is the DB column name used as the CAS version field for ChangePassword optimistic-concurrency control. Composition root uses this constant when wiring cas.Protocol for the user table:
cas.NewProtocol(cas.WithVersionField(accesscore.PasswordVersionField))
const ProbeRepoReady healthz.ProbeName = "accesscore_repo_ready"
ProbeRepoReady is the canonical readiness probe name for the accesscore cell's primary repository. The "_ready" suffix is required by the PROBENAME-SEALED-FUNNEL-01 convention.
Variables ¶
This section is empty.
Functions ¶
func DefaultRefreshPolicy ¶
DefaultRefreshPolicy returns the canonical refresh.Policy used by both the in-memory and PG refresh stores. composition root + integration tests both call this so any policy bump lives in one place.
func RegisterReadiness ¶
func RegisterReadiness(reg cell.Registrar, p healthz.RepoProber) error
RegisterReadiness registers a healthz.RepoProber implementation as the cell-level repository readiness probe on the given Registrar. This is the sole sanctioned entry point — handwritten cell code must NOT call reg.RegisterReadiness directly with a bare string (locked by PROBENAME-SEALED-FUNNEL-01).
Emitter health probes are NOT generated per-cell: they go through the shared kernel funnel cell.RegisterEmitterHealthProbes(reg, emitter), which handles the healthz.ProbeSet assertion and typed-nil guard in one place.
Types ¶
type AccessCore ¶
AccessCore is the accesscore Cell implementation. +cell:listener:ref=cell.PrimaryListener,prefix=/api/v1/access +cell:listener:ref=cell.InternalListener,prefix=/internal/v1/access
func NewAccessCore ¶
func NewAccessCore(clk clock.Clock, opts ...Option) *AccessCore
NewAccessCore creates a new AccessCore Cell.
func (*AccessCore) Authorizer ¶
func (c *AccessCore) Authorizer() auth.Authorizer
Authorizer returns the authorization-decide service (implements auth.Authorizer).
func (*AccessCore) RecordBootstrapAuthFail ¶
func (c *AccessCore) RecordBootstrapAuthFail(ctx context.Context, reason string, clientIPHash redaction.IPHash) error
RecordBootstrapAuthFail records a bootstrap authentication failure by emitting event.auth.bootstrap-failed.v1 via the setup slice's outbox.
Intended caller: the composition-root bootstrap-auth observer closure in cellmodules/accesscore (Wave-1 #1423). The observer is invoked by runtime/auth.NewBootstrapMiddleware after a 401/429 is written. Do not call this method from cells/ or runtime/ code.
The setup service is only available after Init completes; the observer runs after HTTP servers start, so this is always safe.
Returns an error if the setup service has not been initialized yet (Init not called) or if emitting the event fails. Callers (the observer closure) log the error and continue — the compliance chain is best-effort for the observer path, durable via outbox row persistence.
func (*AccessCore) TokenVerifier ¶
func (c *AccessCore) TokenVerifier() kauth.IntentTokenVerifier
TokenVerifier returns the session-validate service. It satisfies kauth.IntentTokenVerifier so it can be plugged into AuthMiddleware without a runtime type assertion.
type NoopSetupLock ¶
type NoopSetupLock struct{}
NoopSetupLock is the public typed marker memstore composition roots wire to satisfy the mandatory WithSetupLock option. Memstore mode does not need a separate cross-process lock because memTxRunner.RunInTx holds store.mu for the entire transaction closure (cells/accesscore/internal/mem/store.go memTxRunner.RunInTx), which by itself serializes all in-process goroutines equivalently to PG SELECT FOR UPDATE held until commit.
PG composition roots MUST use accesspg.NewBundle(pool, txm, clk).SetupLock() instead. Wiring NoopSetupLock in PG mode is an upstream-Soft misconfiguration — the type system here cannot distinguish "right shape per mode".
type Option ¶
type Option func(*AccessCore)
Option configures an AccessCore Cell.
func WithBootstrapAuth ¶
WithBootstrapAuth injects the per-route replacement authentication middleware for the admin setup endpoint (POST /api/v1/access/setup/admin).
The composition root passes runtime/auth.NewBootstrapMiddleware so that the endpoint is gated by Basic Auth credentials from GOCELL_BOOTSTRAP_ADMIN_* env vars (D5: env creds authenticate the operator; request body defines the admin identity). This applies in both bootstrap and interactive modes — the operator Basic Auth credential (ADR §D2) makes the protection a permanent requirement, not an interactive-only feature.
REQUIRED: Init() returns ErrCellInvalidConfig when nil. The closed contract established by codegen + runtime/auth.Route.BootstrapAuth requires this to be wired by the composition root before slice initialisation.
func WithCASProtocol ¶
WithCASProtocol injects the CAS Protocol used by the ChangePassword path (S6 CHANGEPASSWORD-CONCURRENT-SEMANTICS-01). The Protocol declares which DB column carries the monotonic version counter and which conflict policy to apply on mismatch.
REQUIRED: initValidate() rejects nil with ErrCellInvalidConfig so that the cell will not start without a properly-configured CAS primitive. Composition root constructs the Protocol via cas.NewProtocol and passes it here; cells must not construct it directly (CAS-PROTOCOL-COMPOSITION-ROOT-01 archtest enforces this).
Both bare-nil and typed-nil *cas.Protocol are rejected at phase0.
func WithConfigEventCollector ¶
func WithConfigEventCollector(collector obmetrics.ConfigEventCollector) Option
WithConfigEventCollector injects config-event consumer process metrics.
func WithConfigGetter ¶
func WithConfigGetter(c ports.ConfigGetter) Option
WithConfigGetter injects the ConfigGetter used by the configreceive slice to fetch the current config entry value from configcore after an upsert event (contract: http.config.internal.get.v1). When not set the slice operates in log-only mode — no cross-cell HTTP call is made.
Tests and composition roots inject an implementation directly. Concrete factories live in cell-owned adapter subpackages so the root Cell API stays port-oriented.
func WithCursorCodec ¶
func WithCursorCodec(codec *query.CursorCodec) Option
WithCursorCodec sets the cursor codec for pagination. Required in durable mode.
func WithEmitter ¶
func WithEmitter(e outbox.CellEmitter) Option
WithEmitter injects a pre-composed outbox.CellEmitter directly into the Cell. Preferred path for tests and for composition roots that have already built a CellEmitter (e.g. outbox.DemoCellEmitter(), a recorder via outboxtest.Recorder.CellEmitter(), or a wrapped emitter via outbox.WrapEmitterForCell).
Mutually exclusive with WithOutboxDeps — setting both causes Init() to fail fast with ErrCellInvalidConfig. Durability for L2 slice upgrades is derived from the emitter's Durable() method.
ref: kubernetes/client-go rest.RESTClientFor — factory composes the typed client; resulting struct does not retain raw config fields.
func WithJWTIssuer ¶
WithJWTIssuer sets the RS256 JWT issuer for token signing.
func WithJWTVerifier ¶
func WithJWTVerifier(verifier *auth.JWTVerifier) Option
WithJWTVerifier sets the RS256 JWT verifier for token validation.
func WithLockoutMetrics ¶
func WithLockoutMetrics(rec accountlockout.MetricsRecorder) Option
WithLockoutMetrics injects the auto-lockout observability recorder. The composition root constructs auth.NewAccountLockoutMetrics(p) and passes the result here. Nil is silently ignored (mem/demo mode falls back to a no-op recorder inside accountlockout.NewService).
func WithMemBundle ¶
WithMemBundle is the sole public entry point for mem-mode wiring of (UserRepository, RoleRepository, SetupLock, TxRunner). All four primitives come from a single backing mem.Store via accessmem.NewBundle — mis-pairing (e.g. a non-store-paired TxRunner) is inexpressible at compile time outside the cells/accesscore/mem package.
See cells/accesscore/mem.Bundle godoc for the Hard funnel rationale.
func WithMetricsProvider ¶
WithMetricsProvider sets the metrics provider used by the DirectEmitter and refresh-token GC worker.
func WithOutboxDeps ¶
func WithOutboxDeps(pub outbox.CellPublisher, writer outbox.CellWriter) Option
WithOutboxDeps 注入 sealed CellPublisher 和 CellWriter,由 composition root 通过 outbox.WrapPublisherForCell / outbox.WrapWriterForCell 包装得到。 框架在 Init() 时通过 outbox.ResolveEmitter 将二者组合为 outbox.Emitter, 并应用 cell 的 durability-mode 策略。
详见 ADR 202605101900-adr-cell-raw-infra-sealed-marker §D1。
Accumulative: a nil argument leaves the previously-set value in place, so `WithOutboxDeps(pub, nil)` and `WithOutboxDeps(nil, writer)` may be called separately to wire publisher and writer independently. The pairing rules in ResolveEmitter still apply (demo mode allows publisher-only; durable mode requires real writer + txRunner).
Does NOT clear previously-set deps: `WithOutboxDeps(nil, nil)` is a no-op, not a reset. To switch between direct-injection (WithEmitter) and composed (WithOutboxDeps) paths, construct a fresh Cell instead of trying to toggle.
Mutually exclusive with WithEmitter — Init() fails fast if both are set.
func WithPGBundle ¶
WithPGBundle is the sole public entry point for PG-mode wiring of (UserRepository, RoleRepository, SetupLock, TxRunner). All four primitives come from the same (pool, txMgr, clk) triple via accesspg.NewBundle.
See cells/accesscore/postgres.Bundle godoc for the Hard funnel rationale.
func WithPasswordHasher ¶
func WithPasswordHasher(h credential.Hasher) Option
WithPasswordHasher overrides the password hasher threaded to the setup and identity-manage services. Defaults to credential.NewProductionHasher() (cost 12); tests and integration harnesses pass credential.NewTestHasher( bcrypt.MinCost) to avoid the ~1.5s/hash cost. A bare/typed-nil hasher is ignored so the production default survives. BCRYPT-COST-FUNNEL-01 rule A2 keeps NewTestHasher out of production code.
func WithRefreshGC ¶
WithRefreshGC enables the refresh-token GC lifecycle worker.
func WithRefreshStore ¶
WithRefreshStore injects the refresh.Store used for opaque refresh token Issue/Rotate/Revoke. Required — Init() fails with ErrCellMissingTokenIssuer when nil. Composition root passes the mem or PG store.
func WithSessionStore ¶
WithSessionStore injects the session.Store used for session lifecycle (create / get / revoke / revokeForSubject). Required — Init() fails with ErrCellInvalidConfig when nil.
Strong-dependency wiring option: both bare-nil and typed-nil session.Store are rejected at phase0 (via sessionStoreNil sentinel). Pass session.NewMemStore or adapters/postgres.NewSessionStore from the composition root.
ref: runtime-api.md §Option 范式分层 — wiring option, nil rejected at phase0.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package accesscoretest provides public test-infrastructure helpers for the accesscore cell.
|
Package accesscoretest provides public test-infrastructure helpers for the accesscore cell. |
|
Package configgetter wires accesscore ConfigGetter adapters.
|
Package configgetter wires accesscore ConfigGetter adapters. |
|
internal
|
|
|
abac
Package abac is the ABAC (Attribute-Based Access Control) policy authoring and persistence model for accesscore.
|
Package abac is the ABAC (Attribute-Based Access Control) policy authoring and persistence model for accesscore. |
|
accountlockout
Package accountlockout is the typed mediator that drives auto-lockout decisions for sessionlogin.
|
Package accountlockout is the typed mediator that drives auto-lockout decisions for sessionlogin. |
|
adapters/http
Package http provides HTTP adapter implementations for accesscore's outbound cross-cell calls.
|
Package http provides HTTP adapter implementations for accesscore's outbound cross-cell calls. |
|
adapters/postgres/internal/pgexec
Package pgexec is the sealed PostgreSQL executor funnel for the accesscore adapter.
|
Package pgexec is the sealed PostgreSQL executor funnel for the accesscore adapter. |
|
adminprovision
Package adminprovision encapsulates the idempotent, race-safe "bring the first admin into existence" domain logic.
|
Package adminprovision encapsulates the idempotent, race-safe "bring the first admin into existence" domain logic. |
|
authzmutate
Package authzmutate is the single entry point for all authz-field mutations on a User aggregate.
|
Package authzmutate is the single entry point for all authz-field mutations on a User aggregate. |
|
credential
Package credential is the single sanctioned holder of accesscore password hashing.
|
Package credential is the single sanctioned holder of accesscore password hashing. |
|
credentialauthority
Package credentialauthority is the read-side funnel for "is this user-bound credential authorized to issue or continue using a session?" decisions.
|
Package credentialauthority is the read-side funnel for "is this user-bound credential authorized to issue or continue using a session?" decisions. |
|
credentialinvalidate
Package credentialinvalidate is the single entry point for credential revocation events: it bumps the user's authz_epoch, revokes all active sessions, and revokes all refresh chains in one ambient transaction.
|
Package credentialinvalidate is the single entry point for credential revocation events: it bumps the user's authz_epoch, revokes all active sessions, and revokes all refresh chains in one ambient transaction. |
|
domain
Package domain contains the accesscore Cell domain models.
|
Package domain contains the accesscore Cell domain models. |
|
dto
Package dto contains accesscore's local typed views of cross-cell event payloads.
|
Package dto contains accesscore's local typed views of cross-cell event payloads. |
|
httpcookie
Package httpcookie delivers the accesscore refresh token as an httpOnly cookie on the session endpoints (login / refresh / logout) and reads it back on refresh.
|
Package httpcookie delivers the accesscore refresh token as an httpOnly cookie on the session endpoints (login / refresh / logout) and reads it back on refresh. |
|
mem
Package mem provides in-memory repository implementations for accesscore.
|
Package mem provides in-memory repository implementations for accesscore. |
|
mem/internal/txlock
Package txlock mints un-forgeable, self-invalidating lock-ownership leases for the mem accesscore store.
|
Package txlock mints un-forgeable, self-invalidating lock-ownership leases for the mem accesscore store. |
|
ports
Package ports defines accesscore's outbound dependency interfaces.
|
Package ports defines accesscore's outbound dependency interfaces. |
|
ports/conformance
Package conformance defines a UserRepository contract acceptance suite shared by all ports.UserRepository implementations (mem, PG, future).
|
Package conformance defines a UserRepository contract acceptance suite shared by all ports.UserRepository implementations (mem, PG, future). |
|
scopedtx
Package scopedtx funnels every tenant-scoped accesscore write (and tenant-scoped read inside a transaction) through a single tenant.WithScope + RunInTx wrapper.
|
Package scopedtx funnels every tenant-scoped accesscore write (and tenant-scoped read inside a transaction) through a single tenant.WithScope + RunInTx wrapper. |
|
sessionmint
Package sessionmint centralizes access-JWT issuance so that login, IssueForUser (change-password flow), and refresh share a single fail-closed "fetch roles → sign access" pipeline.
|
Package sessionmint centralizes access-JWT issuance so that login, IssueForUser (change-password flow), and refresh share a single fail-closed "fetch roles → sign access" pipeline. |
|
testutil
Package testutil provides shared test fixtures for cells/accesscore tests.
|
Package testutil provides shared test fixtures for cells/accesscore tests. |
|
Package mem exposes the typed funnel for mem-backed accesscore wiring.
|
Package mem exposes the typed funnel for mem-backed accesscore wiring. |
|
Package postgres exposes the typed funnel for PostgreSQL-backed accesscore wiring.
|
Package postgres exposes the typed funnel for PostgreSQL-backed accesscore wiring. |
|
slices
|
|
|
authorizationdecide
Package authorizationdecide implements the authorization-decide slice: RBAC-based authorization decisions.
|
Package authorizationdecide implements the authorization-decide slice: RBAC-based authorization decisions. |
|
configreceive
Package configreceive implements the config-receive slice: consumes config state-sync events from configcore.
|
Package configreceive implements the config-receive slice: consumes config state-sync events from configcore. |
|
identitymanage
Package identitymanage implements the identity-manage slice: CRUD + Lock/Unlock user accounts.
|
Package identitymanage implements the identity-manage slice: CRUD + Lock/Unlock user accounts. |
|
rbaccheck
Package rbaccheck implements the rbac-check slice: HasRole / ListRoles queries for a given user.
|
Package rbaccheck implements the rbac-check slice: HasRole / ListRoles queries for a given user. |
|
sessionlogin
Package sessionlogin implements the session-login slice: password-based login with JWT access token and opaque refresh token issuance.
|
Package sessionlogin implements the session-login slice: password-based login with JWT access token and opaque refresh token issuance. |
|
sessionlogout
Package sessionlogout implements the session-logout slice: revokes sessions and publishes revocation events.
|
Package sessionlogout implements the session-logout slice: revokes sessions and publishes revocation events. |
|
sessionrefresh
Package sessionrefresh implements the session-refresh slice: validates an opaque refresh token via refresh.Store and issues a fresh access JWT.
|
Package sessionrefresh implements the session-refresh slice: validates an opaque refresh token via refresh.Store and issues a fresh access JWT. |
|
sessionvalidate
Package sessionvalidate implements the session-validate slice: verifies access tokens and returns Claims.
|
Package sessionvalidate implements the session-validate slice: verifies access tokens and returns Claims. |
|
setup
Package setup implements the interactive first-run admin provisioning slice.
|
Package setup implements the interactive first-run admin provisioning slice. |