Lockspire.Domain.InitialAccessToken (lockspire v1.0.0)

Copy Markdown

Durable initial access token used to gate POST /register when Lockspire.Domain.ServerPolicy.registration_policy == :initial_access_token.

Hash-at-rest reuses Lockspire.Security.Policy.hash_token/1 (sha256 lowercase hex). Plaintext is shown once at mint time only (Phase 28 admin LiveView; out of scope for Phase 25).

Phase 25 ships schema + struct onlyLockspire.Protocol.InitialAccessToken.redeem/1 is Phase 26 (DCR-11). Atomicity for redemption depends on the unique_index([:token_hash]) shipped in Plan 03's lockspire_initial_access_tokens migration.

policy_overrides boundary (T-25-05)

This struct's policy_overrides field carries operator-controlled JSON narrowing the effective DCR allowlists for any registration that uses this IAT. Phase 25 ships the field as opaque storage — narrowing-at-mint validation (override ⊆ server allowlist) is a Phase 28 admin-path concern. The Lockspire.Protocol.DcrPolicy.resolve/3 resolver (Plan 07) does NOT re-validate widening at resolve time per D-18: if a stale override carries an out-of-allowlist value (e.g., policy was tightened after IAT mint), MapSet.intersection/2 naturally drops it — never widens.

Summary

Types

Operator-controlled per-IAT narrowing of the resolver's effective DCR allowlists.

t()

Types

policy_overrides()

@type policy_overrides() :: %{optional(String.t()) => [String.t()]}

Operator-controlled per-IAT narrowing of the resolver's effective DCR allowlists.

String-keyed map (the resolver's override_for/2 looks up string keys) where each value is a list of strings (the resolver's intersect_axis/4 only lets list values pass through; non-list values are treated as "no override"). Known keys mirror the dcr_allowed_* axes:

  • "allowed_scopes"
  • "allowed_grant_types"
  • "allowed_response_types"
  • "allowed_redirect_uri_schemes"
  • "allowed_redirect_uri_hosts"
  • "allowed_token_endpoint_auth_methods"

Pinning the shape here lets Dialyzer catch drift between admin-mint (Phase 28) and resolve-time (Phase 25 Lockspire.Protocol.DcrPolicy.resolve/3). A malformed %{atom_key: "string"} would silently bypass every override under the looser map() | nil typespec — see DCR-09 and the WR-09 follow-up invariant test (Phase 28).

t()

@type t() :: %Lockspire.Domain.InitialAccessToken{
  created_by: String.t() | nil,
  expires_at: DateTime.t() | nil,
  id: integer() | nil,
  inserted_at: DateTime.t() | nil,
  policy_overrides: policy_overrides() | nil,
  revoked_at: DateTime.t() | nil,
  single_use: boolean(),
  token_hash: String.t() | nil,
  updated_at: DateTime.t() | nil,
  used_at: DateTime.t() | nil
}