auth.acquisition/v1alpha1
Rendered from contracts/auth/acquisition/v1alpha1/semantics.md by connectors-docs; the source owns every rule on this page.
- Status: proposed, not implemented.
- Family: auth. Sibling documents: connection, profile, custody, capability, evidence.
- Recorded: 2026-09-08.
1. Identity
| Field | Value |
|---|---|
| Contract | auth.acquisition/v1alpha1 |
| Paths | static_config (deployment binding, no acquisition session); coordinator flows: static_entry, oauth2_authorization_code, oauth2_client_credentials; the CLI grant flows of an oauth2_grant profile: oauth2_authorization_code, oauth2_device_code; reserved here: oauth2_password, workload_identity, exec_plugin, host_issued (session authority has its separate sessions contract) |
| Parties | host coordinator (shared), provider auth implementation (adapter-owned), trusted user interface, custody |
More columns: swipe horizontally, or focus the table and use the arrow keys.
Design responsibility two of four: establish, refresh, revoke, or repair authorization, owned by a shared coordinator plus the provider auth implementation (docs/design.md:527). The coordinator owns lifecycle, state, expiry, concurrency, recovery; the provider implementation owns request construction and response interpretation (docs/design.md:551).
3. Types
Coordinator interfaces have distinct bindings, owned as specified in management. Safe-payload auth.begin and auth.status are host-owned admitted management operations; forwarding additionally needs the complete selected federation binding. auth.complete is a trusted callback/protected-entry port, not ordinary invoke input or a generic forwardable operation. Refresh and revoke retain their private/admitted entry points below (docs/design.md:785):
auth.begin(profile, requested_scopes?, repair_of?: connection) -> AuthorizationAction
auth.complete(acquisition_ref, evidence) -> AcquisitionStatus (trusted completion port; separately bound)
auth.status(acquisition_ref) -> AcquisitionStatus
auth.refresh(connection) -> RefreshOutcome (host-internal, not caller-invocable)
auth.revoke(connection) -> RevocationOutcome (via connections.revoke)
{ "acquisition": "acq_…", "expires_unix_ms": 0,
"action": { "kind": "browser" } }
{ "acquisition": "acq_…", "expires_unix_ms": 0,
"action": { "kind": "protected_entry" } }
{ "acquisition": "acq_…", "state": "pending" }
{ "acquisition": "acq_…", "state": "completed", "connection": "conn_…" }
{ "acquisition": "acq_…", "state": "failed", "reason": "expired | refused_by_provider | identity_mismatch | insufficient_scope | custody_unavailable" }
These ordinary result examples contain no actionable URL. The separately bound trusted UI channel receives the expiring owner-bound URL and protected-entry field schema; status/discovery never reissue them. pending is acquisition state and need not identify a public Connection. A completed acquisition records acknowledged baseline publication, while current connection status is queried separately.
Private provider interface (adapter-owned, never serialized):
begin(registration, requested_access) -> ProviderAuthorizationRequest
exchange(validated_callback_evidence) -> CredentialUpdate
refresh(current_credential_set) -> CredentialUpdate
revoke(current_credential_set) -> RevocationOutcome
CredentialUpdate separates safe metadata (external identity, granted scopes, expiry, kind) from sensitive material (docs/design.md:562).
Outcomes of refresh: refreshed, reauthorization_required, insufficient_scope, invalid_or_revoked, custody_unavailable, uncertain (docs/design.md:600).
4. Rules
4.0 Acquisition paths and required declarations
“Specified” below means this proposed contract defines a path. It does not mean the current host, adapter schema, profile reader or codec implements it. An advertised supported profile needs all of those bindings and its validation/placement implementation. Configuration flags do not promote a reserved flow to supported.
| Flow | Disposition and required declaration | Fields/path not permitted |
|---|---|---|
static_config | Specified deployment binding. Profile declares accepted material/transport shape and admitted validation plan; configuration supplies scoped references, or the explicit no-child-credential binding in profile §4.2. | No auth.begin, acquisition id, protected-entry action, OAuth endpoints, grants, registration or callback. No credential bytes in ordinary configuration/result data. |
static_entry | Specified protected entry. Profile declares expected entry fields/material shape, identity/grant interpretation and validation plan with sources. Host must bind custody, coordinator and admitted protected UI/ingress. | No OAuth endpoints, OAuth grants, redirect or browser PKCE fields, except the acquisition metadata of the OAuth-acquired non-rotating variant below. Protected form submission alone is not completion/publication. |
static_entry, OAuth-acquired non-rotating variant | Specified protected entry whose material a trusted local CLI obtains by OAuth consent before submitting it (architecture-decision-record:oauth-material-as-static-entry). The profile additionally declares nonsecret acquisition metadata: authorize_url, token_url and requested scopes. The entry is exactly {client_id, client_secret, refresh_token}. Inside the capture window the CLI runs an authorization-code grant with a loopback redirect, PKCE S256 and a single-use state, then submits the triple through the ordinary static-entry capture; owner, registry and custody are unchanged. The provider exchanges the refresh token for access tokens held in its process memory only. Admitted only where the provider does not rotate refresh tokens: a refresh answer carrying a different refresh_token is refused as an invalid credential and nothing is stored. | No coordinator begin/complete ports, trusted UI channel, host-owned refresh or RefreshAttempt; no persisted access token; no rotated material; no consent action in ordinary results. |
static_entry, oauth2_grant profile (flows oauth2_authorization_code, oauth2_device_code) | Specified protected entry whose material a trusted local CLI obtains by an OAuth 2.0 grant (architecture-decision-record:oauth2-grants-and-access-token-issue, story:connect-runs-an-oauth2-grant). The profile declares scheme oauth2_grant and an oauth2 block, a connectors.oauth2.ServerDeclaration with nonempty unique grant_types, HTTPS endpoints (a pasted_code redirect included: only a loopback redirect is plain http, on 127.0.0.1 or localhost) and, for device_code, a device_authorization_url; the metadata authority records it with DeclareAuthorizationServer when the profile is recorded. client_id is the declaration's, else the instance configuration's oauth2_client_id; neither, or two that differ, is invalid_configuration before any network step. A confidential client's secret is the protected source {"client_secret": "…"}; a public client takes no source. Inside the capture window the CLI runs the authorization code with PKCE S256 over the declared redirect kind, or with --device the device grant (RFC 8628, or the device dialect the composition registers for the profile id), and submits the entry {client_id, access_token, refresh_token, expires_at_ms, scopes, id_token?} (at most 64 KiB, the only bound on each of its tokens) through the ordinary static-entry capture. The owner validates it without a refresh: an unexpired access token, the required scopes within the granted scopes, and the identity from the id_token the token endpoint returned (OpenID Connect Core 1.0 §3.1.3.7 item 6) or from the declared identity request with the access token. A grant that fails, times out or is cancelled stores nothing. Refused on read-only custody and for credential reuse. | No coordinator begin/complete ports, trusted UI channel or RefreshAttempt (refresh is story:owner-rotating-refresh); no token, code, verifier or client secret in any result, log, error or terminal line: the consent address and the device user code with its verification address are the only lines shown. |
oauth2_authorization_code | Specified browser flow. Require nonempty authorize_url and token_url, vendor sources, authorization_code grant, explicit PKCE choice and vendor justification, registration kind and sourced client-auth method/policy, callback binding, token/identity/grant interpretation, validation and refresh policy. Refresh grant only when supported by the cited profile. | No inferred endpoints, token encoding or PKCE from another provider. Registration values remain configuration/custody. |
oauth2_client_credentials | Specified noninteractive flow. Require nonempty token_url, vendor sources, client_credentials grant, configured confidential registration and declared client-auth/token/identity/grant interpretation and validation. This first profile does not request/accept refresh-token behavior; obtaining another credential set is a new admitted acquisition. | authorize_url, callback/redirect, PKCE, browser action and refresh_token grant are rejected; no trusted browser channel is required. |
oauth2_password, workload_identity, exec_plugin, host_issued | Reserved in this acquisition contract. Preserve names for reviewed future bindings; reject selection/advertisement as implemented here. Exec/session mechanism constraints elsewhere remain requirements for those future/separate profiles. | No inferred generic auth.begin/completion path or flag-only enablement. |
More columns: swipe horizontally, or focus the table and use the arrow keys.
All profile declarations remain credential/reference-free, with bounded sources and material/validation descriptions. Required safe endpoint strings must meet the selected host's reviewed egress policy; their exact provider values and request formats need cited vendor evidence. This matrix changes no actual adapter-kind schema.
static_config is activation of an explicitly configured binding, not a pasted credential or a managed acquisition. The host separately admits activation/revalidation, resolves only the configured references at that boundary, captures coherent immutable material, validates the candidate and publishes its metadata under the same binding/revocation fence as other credential paths. Read-only custody need not copy an externally supplied secret into a writable store; its immutable snapshot must remain available for dispatch. Unavailable custody/validation refuses activation. Listing does not activate, run an exec helper or resolve secrets. Changes require fresh capture/validation; filenames and revisions do not prove identity continuity. Binding metadata publication must be acknowledged, but no fictitious acquisition/completed event is emitted. Static no-credential profiles instead validate their explicit destination/route binding and applicable checks, with no capture, external identity or custody write. No static binding automatically refreshes or starts a browser.
Managed-flow sequence (specializes docs/design.md:543-549; static_config follows the preceding activation path):
- Host admits
auth.beginagainst caller, tenant, target connection (for repair), profile, and configured registration. - Coordinator creates one-purpose expiring state correlated to the request. Interactive flows bind the allowed callback or entry URL; noninteractive client credentials have no completion URL. State is single-use.
- For browser/protected-entry flows, the owning coordinator delivers the one-use action URL only to the separately admitted trusted UI channel. Ordinary results carry acquisition ref, expiry and action kind; no usable completion authority. Without the needed protected delivery/ingress binding that interactive flow is unavailable. Non-interactive client credentials skip this UI step and may complete in begin through their admitted registration/provider/custody path. Current authority, owner, expiry and one-use checks remain required at completion.
- Interactive completion evidence (OAuth callback code and state, or a posted static credential) is validated for correlation, expiry, one-time use. Client credentials exchanges directly from its admitted registration. The provider implementation performs the applicable exchange/entry validation and validates returned identity, token kind and granted scopes against the profile minimum, recording actual grants; optional requested grants are not mandatory publication requirements (profile §4.1).
- Repair: the returned external identity must match the bound one, else
identity_mismatchand the connection remains in its prior state. - Baseline validation, including any explicitly mandatory global verification, must succeed. Custody stores the complete credential set durably before the coordinator publishes its active reference under the current binding/revocation fence; both acknowledgements precede
completed. Optional verification does not prevent publication; required operation-only checks still gate those operations. A failed independent repair preserves the prior binding subject to its own current validity, expiry and revocation. - The caller receives the connection ref and safe status. Completion never executes a previously failed business operation.
4.1 Refresh exclusion, authorization and recovery
Refresh is triggered only through the selected auth profile's separately admitted maintenance policy (auth.evidence, expiry near), an issue-time expiry check (an access token issue whose published token expires within the profile's refresh lead) or an explicitly supported execution binding permitting a provider 401; it is never caller-invocable. Neither trigger grants business redispatch by itself. The host coordinator and its metadata binding own cross-replica exclusion and the durable refresh ledger. Custody owns only immutable sensitive versions. A custody CAS or an expiring lease alone cannot authorize an exchange (docs/design.md:596-600). For oauth2_grant the owner runs this sequence through the connectors.refresh ledger.
The serialization key is one host-private CredentialGeneration from the shared ESS model: an immutable material capture bound to an instance, connection, profile and provider authority, with an expected external identity. It is neither a secret-derived identifier, a file path, nor a custody version. Capturing bytes does not validate identity. A refresh result receives a new generation even if the account is unchanged; validation and dispatch obey evidence. The host must prevent the same rotating refresh material from entering two independently refreshable generations or connection bindings. Repeated observations/restarts reuse the existing private capture association where the material is unchanged; aliases that cannot be established safely are refused. UUID uniqueness alone cannot prevent duplicate use of the same token.
Each attempt has a unique id, a source generation, a current owner and a non-reusable ownership token (fence), and the private connection publication fence it expects. All replicas use one linearizable coordinator authority for this metadata; process mutexes, unlocked files and disconnected local ledgers are insufficient. These are required logical operations, not a choice of database:
| Atomic coordinator operation | Preconditions and durable effect |
|---|---|
| Reserve | Compare the current source/binding and non-revocation; acquire the source's unique reservation and create Reserved. A contender observes the existing attempt and waits or refuses; it cannot send. |
| Authorize exchange | Compare Reserved, current owner/fence, source/binding and non-revocation; atomically mark Authorized, consume that source's one-exchange authority and invalidate its outstanding dispatch admissions before any possible provider call. Only the original live invocation that observes a committed success may send once. A timeout/unknown commit result grants no send. Re-reading or replaying success never grants another send. Provider clients must disable implicit retries. |
| Fence before authorization | CAS Reserved → Fenced against authorization, invalidate the old owner, then release the reservation for a new attempt on the still-current source. Lease expiry merely permits attempting this CAS. The old owner must fail its later authorization. |
| Store response | After receiving and validating a complete candidate under F05 and durably storing the whole credential set, atomically attach its new generation to the Authorized attempt under the current fence and mark ResponseStored. A loose orphan blob is not a committed response record. |
| Resolve authorized owner loss | Atomically inspect/CAS against response storage. If ResponseStored won, recover publication only. Otherwise make Authorized → Uncertain; the source remains consumed. No lease transfer, restart, unchanged active pointer or claimed absence of send permits another exchange. |
| Recover committed response | Fence the old publication owner and move ResponseStored → Recovering with a new owner/token. Recover exactly the recorded candidate, rechecking custody and evidence. This grants publication only. In this first profile a lost recovery owner causes discard/repair, rather than a second recovery transfer. |
| Publish | In one metadata transaction compare attempt state, current owner/fence, expected source and private publication fence, exact candidate, current F05 evidence and non-revocation. Replace active version/generation, advance the private publication fence, mark Published, and invalidate all old-generation dispatch admissions together. |
| Revoke or replace binding | Use the same serialized metadata authority as publish/authorize, advance the private publication fence and invalidate outstanding dispatch admissions. Revocation is monotonic for that binding; a stale refresh cannot clear it. Reauthorization creates a new private publication fence and fresh material. |
More columns: swipe horizontally, or focus the table and use the arrow keys.
A successful authorization is the irreversible boundary even when the process dies before the network send. After it, uncertainty is conservative: the token may have been consumed. Transport failure, negative-looking provider responses, candidate validation failure, custody failure after exchange, response loss and local commit ambiguity never reopen source authorization. The two exceptions: an exchange the token endpoint refused with HTTP 429, and one whose transport proved that no byte of the request left (name resolution, connection or TLS handshake failed), are released (connectors.refresh.ReleaseRateLimited), not quarantined, and their source may be reserved again; no other negative response or transport failure reopens source authorization. An unusable result requires repair/reauthorization. No retry exception based on alleged provider grace or token reuse is selected here.
Publication uses a strict cutoff: once it commits, no admission pinned to the old generation may open a new provider dispatch. A dispatch already opened before the cutoff is not retroactively undone. Pinning preserves the validated bytes; it cannot override known revocation, expiry or invalidation. At exchange authorization the old generation's refresh authority is consumed; old-generation dispatch is withheld atomically with that authorization while refresh is unresolved. Publication makes the new validated generation available; uncertainty leaves refresh and dispatch blocked pending repair.
If revocation commits first, publication refuses. If publication commits first, revocation applies to the newly active generation and blocks later dispatch. Revocation cannot undo an already-authorized provider exchange; it prevents its result from restoring authority. A stale owner's publication fails even if its response is valid. A committed publication whose reply was lost is observed from the ledger, never repeated as a new exchange. Superseded, revoked or quarantined source records retain the fact of consumed authorization; cleanup must not allow their identity or material to be reintroduced as refreshable.
Required durability includes surviving host restart with reservation/consumption, ownership and publication decisions intact. A binding that cannot provide the stated atomicity, fencing and durable recovery must refuse rotating refresh; falling back to per-process locking or a second exchange is forbidden. In-memory test bindings may simulate the protocol but claim no restart durability. Safe outcome names remain refreshed, reauthorization_required, insufficient_scope, invalid_or_revoked, custody_unavailable and uncertain; detailed ledger refusals below are private, not new public error envelopes. uncertain records the exchange observation and requires reauthorization, never “not configured.”
Scope and identity checks apply to refresh results; rotation never widens permission. Refresh success never retries a business write (docs/design.md:600; operations mutation profile). The proposed read-refresh-once/v1alpha1 binding selects the bounded direct-read exception only for its explicitly advertised v1alpha2 combined profile. It participates in one source attempt, observes its published current successor and requires a fresh dispatch admission within the original invocation's time, byte and permission-call ledger. A waiter sends no token request; observing or recovering publication never replays an exchange. Existing/legacy profiles do not silently acquire this behavior, and no current adapter implements the binding.
4.2 Failure matrix
| Observation | Required next action | Another exchange with the source? |
|---|---|---|
| Two replicas before authorization | One reservation/authorization; contender waits and re-reads active generation | No |
Owner lost while durably Reserved | Fence old owner atomically, then reserve a new attempt | Yes, only after the fence wins before authorization |
| Authorization commit unknown, or committed but owner lost before send | Quarantine/repair unless committed response is recoverable | No |
| Provider may have consumed token; response lost | uncertain, require reauthorization, retain consumed record | No |
| Response exists only in memory, or custody write outcome unknown | Quarantine/repair; orphan cleanup cannot infer a result | No |
| Committed candidate/response, owner lost before publish | Fence and recover publication of that candidate; check current evidence | No |
| Candidate identity/scope invalid, unavailable or expired | Refuse publication, discard/repair; retain consumed source | No |
| Stale owner publishes after recovery transfer | Ownership conflict; current owner may publish the same candidate | No |
| Revocation or binding replacement wins publication race | Refuse publication; never clear revocation or overwrite successor | No |
| Publication committed, response to owner lost | Observe Published and current binding; no exchange replay | No |
| Stored-response recovery owner also lost | Discard candidate/repair in this first profile | No |
More columns: swipe horizontally, or focus the table and use the arrow keys.
Static entry (static_entry): the protected entry page posts to the coordinator over the host's admitted ingress; the value is written to custody and the connection created; the page and URL expire. For http_basic profiles the entry has two fields (user half, secret half) as in the Atlassian API token (../connectors/providers/jira.toml, user_env).
Client credentials (oauth2_client_credentials): no browser; begin performs the exchange with the configured registration and returns a completed status directly.
4.3 Acquisition owner, consumption and terminal records
Acquisition has a non-reusable opaque ref at its one coordinator, instance and
immutable AuthProfile record references, admitted owner/origin/registration,
original expiry and expected private Connection publication fence. It references
one fixed target_connection_ref and optional repair/yielded Connections, owning
none. On repair, target equals repair; on creation, target is the privately
allocated binding, allocated before Begin/capture/custody. Completed's result equals
that fixed target. Concurrent attempts
cannot bypass current publication fencing. Initial creation privately allocates
its fixed binding before custody; no public pending Connection is required.
Static_config creates no Acquisition.
Internally Pending → Completing → Completed; failure/expiry can terminate either
nonterminal state. Pending→Completing durably consumes the protected one-use
correlation under current owner/admission/expiry checks before exchange. Both states
project as public pending. Only the definite live winner can perform the original
admitted exchange; observing consumed state never grants another send. Unknown
consumption acknowledgement permits owner observation or refusal, not exchange on
guessed success. Unknown exchange becomes Failed with an uncertainty reason and
no resend. A known protected result may be finalized under the original publication
protocol without another exchange.
Completing→Completed requires definitely acknowledged custody and guarded baseline
Connection publication before original expiry. The yielded reference is present
exactly on Completed. Failure/expiry preserves an established repair target and
cannot overwrite a winner or reverse revoke. At now >= expires_at, known expiry
permits Expired and prevents further exchange/publication. Expired projects as
the existing public failed result with reason expired, not a new public status.
Unknown time/acknowledgement refuses progress, not guessed validity or expiry.
Terminal refs, consumption and publication outcome facts have no automatic deletion in this first binding. Protected callback/entry payload remains only while required by admitted completion/recovery and is erased when terminal with no remaining use. It is never a replay credential. Finite capacity refuses new acquisitions rather than evicting consumed/terminal safety facts. Protected-ingress codecs remain a separate binding obligation. auth_bindings.yaml models causal lifecycle/references; verification separates model checks from actual atomic correlation/publication execution.
4.4 Access token issue
The owner issues an access token of an oauth2_grant connection to a local
caller: connections token (CLI)
or the library function connectors_host::local::access_token::access_token.
The caller names a connection and a delivery; it cannot ask for a refresh.
- The issue reads the connection's published generation. A Stale connection is
refused as
reauthorization_requiredwithout a request; a profile that is notoauth2_grantis refused as forbidden. - While the published access token stays valid for longer than the
declaration's
refresh_lead_seconds, it is issued as it is (current). Otherwise the issue first runs the §4.1 sequence once, under the sameconnectors.refreshledger serialization as every other refresh, and issues the generation that sequence published (refreshed). The reservation names the generation the issue read, in the transaction that reserves: when another caller's refresh already published a successor, the issue sends nothing and issues the successor. A provider whose fresh token already expires within the lead is issued that token; an issue never runs a second exchange. A token inside the lead that this owner cannot refresh (a confidential client, whose secret the stored entry does not hold) is refused as forbidden. A refresh that does not publish refuses the issue with the refresh's safe outcome (§4.1), and a token already past its expiry with nothing published in its place is refused as unavailable. - Each successful issue is recorded as one
connectors.oauth2.AccessTokenIssue(IssueAccessToken, ESS) in the transaction of the connection's refresh ledger: the issued generation, the refresh attempt that published it on a refreshed issue, the expiry, the granted scopes and the delivery (metadata_only,revealed,libraryorconsumer_launch). The record carries no token. When a refresh or a repair replaces the generation between its read and the record, the issue reads it again. An issue whose generation a repair rather than a refresh published is recorded ascurrent. A refusal records nothing. - The token leaves the owner only in the transport's secret frame, and only
for a delivery that carries it (
revealed,library), or, forconsumer_launch, sealed on the launched consumer's descriptor 3 (consumer launch §9); only the owner's own launch issuesconsumer_launch.metadata_onlyanswers the metadata alone. No log, error,DebugorDisplayoutput names the token.
| Refusal | Owner failure |
|---|---|
profile not oauth2_grant, or not in the instance's permissions | forbidden |
| token inside the lead of a confidential client | forbidden |
reauthorization_required or uncertain | service_failure |
insufficient_scope | not_granted |
invalid_or_revoked | revoked |
custody_unavailable | custody_unavailable |
token endpoint 429 | unavailable with the rate_limited service code and Retry-After in whole seconds |
unavailable: the token endpoint was not reached, nothing was sent | unavailable |
| token expired, nothing published in its place | unavailable |
| the caller's deadline passed before the issue was recorded | timeout; the refresh it ran stays published |
More columns: swipe horizontally, or focus the table and use the arrow keys.
5. Ordering, limits
| Concern | Rule |
|---|---|
| Acquisition expiry | first-profile ceilings 600 s browser / 300 s entry; lower admitted values permitted, original deadline never slides |
| Concurrent begins for the same connection repair | allowed; the first completion wins; later completions are expired |
| Refresh skew | refresh before expiry by a declared margin (old Jira used a skew; value to be measured, connector-oauth header) |
| Retry | provider exchange is not retried on unknown outcome |
More columns: swipe horizontally, or focus the table and use the arrow keys.
6. Conformance scenarios (docs/design.md:985)
- Callback with mismatched state → refused, no exchange call on the fake provider.
- Callback replayed after completion → refused, one connection exists.
- Expired acquisition completed →
expired. - Repair returning a different account id →
identity_mismatch; no replacement publication. Prior material is usable only if independently still current, valid and unconsumed. - Fake provider returns fewer scopes than
minimum→insufficient_scope; no candidate publication/completed (ordinary validation precedes custody write). Optional requested write scope omitted with minimum read scope present → baseline publication/completed permitted, read eligible and write insufficient_scope. - Two replicas refresh the same generation concurrently → one committed authorization and at most one exchange; both eventually observe the new generation or the same refusal. Exercise the coordinator binding, not a fake custody lease.
- Owner loss before authorization → fence old attempt before successor reservation. Loss after authorization (including before send) → no second exchange.
- Committed response recovery → current publication owner succeeds; stale owner is refused; revocation/identity replacement wins without resurrection.
- Publication cutoff → old-generation admissions cannot newly dispatch; already-opened transport is not rolled back.
- Fake provider rotates then drops the response →
uncertain; connectionreauthorization_required; no second refresh call. - Two retry-bound read invocations receive 401 for the same generation → one committed token-exchange authorization globally; each live invocation may observe the same published successor and independently establish its fresh admission within its own original remaining budgets. An expired/cancelled invocation never resumes when publication completes. This is the read binding's textual composition, not an implemented business retry.
- Swap the custody binding (in-memory ↔ file) → provider auth implementation unchanged (
docs/design.md:1014).
7. Compatibility
- Old
connect_session_*methods map ontoauth.begin/auth.status; a facade can preserve names. - Old per-integration OAuth copies are not carried.
- Service compatibility is authoritative for the binding. Safe begin/status payloads require the selected management schema; protected entry and completion evidence need their dedicated trusted binding. A unary version does not supply callback protection.
8. SDK and host obligations
| Obligation | Where |
|---|---|
| Coordinator module with acquisition state store (expiring, one-use), durable per-generation refresh ledger, atomic authorization/fencing and publication/revocation | new host module |
| Separately admitted protected UI delivery and callback/entry ingress with bounded bodies and owner/correlation/one-use checks | future host binding; exact codec/paths remain unselected, no generic invocation or discovery disclosure |
SDK trait for the private provider interface (begin/exchange/refresh/revoke) with a CredentialUpdate type that has no Debug/Serialize on its sensitive half (pattern: Secret, crates/connectors-sdk/src/lib.rs:33-34) | crates/connectors-sdk |
| Shared OAuth mechanics (state, PKCE optional, token response parsing) as SDK helpers without owning the transport | crates/connectors-sdk (old rationale: connector-oauth header, "does not own the request") |
More columns: swipe horizontally, or focus the table and use the arrow keys.
9. ESS entities
| Entity | Notes |
|---|---|
Acquisition | connectors.auth_bindings.Acquisition: Pending/Completing/Completed/Failed/Expired and instance/profile/fixed target/optional repair/result references under §4.3. Completing is internal consumption, not a new public status. |
CredentialSet | defined in custody; custody version is not refresh identity |
RefreshAttempt | ESS: Reserved → Authorized → ResponseStored → Published, pre-authorization fencing, stored-response recovery, terminal uncertainty/discard; exactly one source-generation reference |
CredentialGeneration | shared ESS: host-private immutable capture; expected identity, not verified evidence |
AccessTokenIssue | ESS: one Issued record per access token issue under §4.4, naming its generation, its refresh attempt when it refreshed, and its delivery; never the token |
| Typed coordinator decisions | Trusted transient values selected inside the atomic operation; never caller authority or reusable approval |
| Trusted UI and callback host identity | UNMAPPED |
More columns: swipe horizontally, or focus the table and use the arrow keys.
The verification record separates compiled authored scenarios from runtime obligations. ESS checks typed values, references and lifecycle causation; it does not prove per-source uniqueness, real lease fencing, actual send counts, byte pinning or cross-entity atomicity. These remain explicit UNMAPPED implementation obligations.
10. Open decisions
| Decision | Default taken |
|---|---|
| Callback host placement in federation | completion terminates at the one owning logical coordinator through a separately admitted protected ingress; safe begin/status use the selected management binding; no state copying or automatic reroute/resend (management) |
| PKCE choice | explicit per authorization-code profile with vendor justification; no inherited/default choice; absent for other first-profile flows |
| Expiry values | 600 s / 300 s first-profile defaults |
More columns: swipe horizontally, or focus the table and use the arrow keys.
Persistence ownership is consolidated in design §31. AcquisitionCoordinatorPort and RefreshCoordinatorPort retain distinct state ownership while using the required connection authority fence; CustodyPort owns only sensitive immutable versions. This inventory does not supply a backend or execute its atomicity predicates.