Repository navigation
Conversation
…fault flag (Abilityai/trinity-enterprise#623) An agent on one Trinity calls an agent on another and is authenticated as ITSELF — by its own key under AAuth's agent-identity-only mode — with no bearer secret of either instance configured on the other. Prototype for the 2026-09-18 call with the protocol's author; `aauth_prototype_enabled` is OFF by default and flag-off behaviour is unchanged. Each instance is its own Agent Provider (AAuth self-hosted bootstrap): it publishes `/.well-known/aauth-{agent,jwks,resource}.json`, self-issues `aa-agent+jwt` tokens binding a per-agent key via `cnf.jwk`, and signs requests per RFC 9421 with the key conveyed by `Signature-Key: sig=jwt;jwt="…"`. Inbound (`POST /a2a/{name}`): a request carrying `Signature-Key` while AAuth is live takes a separate entry point, so the bearer dependency — which 401s a request with no token before any handler runs — is not made conditional and the bearer path stays byte-identical. Verification runs before the body is parsed as JSON-RPC, cheapest-and-local first: header shape → token claims → exposure + trusted-issuer pre-gate → issuer metadata/JWKS (pinned public-HTTPS egress, same-origin `jwks_uri`, one 8 s deadline) → token signature → HTTP signature (which covers the Content-Digest HEADER) → body digest → replay claim. Only then is the body read. An AAuth caller never becomes a Trinity `User`: access is exposure plus an EXPLICIT allow-list entry, fail-closed, and no owner role travels with it. Refusals answer RFC 9457 problem details with `Signature-Error`; a verified-but-unlisted caller gets 403. Two properties are load-bearing and easy to lose: * B never fetches metadata for an issuer host no operator listed, so an unauthenticated caller cannot use this backend as a fetcher; and * a refusal is only auditable once the token's own signature verifies — before that `sub` is a string the caller typed, and attributing an audit row to it would hand any stranger the audit table. Outbound: a registered endpoint may be marked `auth_scheme: aauth` (no credential — switching to it clears one, non-default ports refused). Only the agent's OWN key may place such a call: `AuthorizedAgentByName` also admits the owner, shared users and admins, and for AAuth that would let a human make the backend sign as the agent. The request is signed over the exact bytes sent, to the registered host rather than the pinned IP. Also here: the served card declares `aauth` AFTER `bearerAuth` (an AAuth-unaware client that takes the first entry is unaffected); a new `external_agent` audit actor type, so a remote caller is not recorded as the platform itself; and a path-relative card fetch for aauth endpoints, because a Trinity peer serves its card under `/a2a/{name}/` while the client only ever looked at the origin root — which is why Trinity-to-Trinity A2A did not work at all. The general fix for that wants its own public issue. Verified live between two independent instances (the second in its own Docker daemon — see the learnings entry on why a shared daemon eats the first one's agent volumes): `whoami.aauth.dev`, a verifier that is not ours, echoes the calling agent's identity; the remote task runs; an unlisted identity is refused 403; a tampered body is refused 401; a bearer call to the same endpoint still works. A request signed by the JS reference `@hellocoop/httpsig` verifies here (checked-in vector), and ours verifies there. Stated prototype limits: one software-held instance key (AES-256-GCM at rest, no rotation), the backend signs on the agent's behalf, identity is keyed by agent name, `message/stream` is refused for AAuth callers, and inbound AAuth is inert without the private allow-list module. Requirement: docs/memory/requirements/mcp.md §32.6. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…trinity-enterprise#623) The end-to-end flow, written to be read by someone who did not build it: roles, layers, public surface, config state, and the four flows (enablement, outbound sign, inbound verify-and-decide, served card) with the ordering constraints spelled out — why the trusted-issuer pre-gate runs before any network I/O, why a refusal only becomes auditable once the token verifies, and why the body is read after the signature that covers its digest rather than before. Also states the trust model plainly: what the callee actually verifies (key possession + this exact request), what it does not (who runs the peer), that the unit of trust is the peer instance rather than one agent, and that authorization stays coarse — an allow-list entry means the peer may ask this agent for anything it can do. Closes with the prototype's limits and the diagrams worth drawing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…prototype onto current dev (#3644)
…pricing reads after the dev merge (#3644) dev's inbound door now asks db.get_a2a_scope (trinity-enterprise#838) and, for a bearer-less caller, db.get_nevermined_config_with_key (ent#679), and it resolves its principal through get_user_or_anonymous — which calls get_current_user instead of depending on it, so the fixture's override has to sit on the optional dependency. 17 of 30 tests in this file failed on the merged tree for exactly those three reasons; no product code changes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…agent key (abilityai/trinity-enterprise#878) Checkpoint A of the person-token binding leg (Hellō person server). * services/aauth/person_token.py — person_token()/resume()/held_token(): signed POST (#623 profile) to the PS person_token_endpoint, 200 → held per (our_issuer, ps_issuer, agent, resource) until exp − 5 min and only while cnf is the agent's current key; 202 → Redis pending record (hashed handle, per-agent cap 3, no token stored) + url/code/link; the draft's deferred-response state machine on resume (202/200/403/408/410/ 429, cross-key reissue); audit_details() never carries token/handle/url/code. * services/aauth/person_server.py — aauth-person.json discovery: four required fields, issuer equality, token endpoints same-origin, jwks_uri may be off-host; per-worker caches, single-flight. * services/aauth/requirement.py — AAuth-Requirement (RFC 8941 dictionary), Retry-After, Crockford code and interaction-URL rules. * services/aauth/egress.py — request_capped(): every status with headers and a capped body behind the same pinned/no-redirect/identity/wire-cap guards as the A2A reader; a2a_client.py untouched (T5). * services/aauth/signer.py — T6: the per-agent key is HKDF-derived from the instance seed per (issuer, agent, hour window) so both workers hold one key; min_remaining keeps a challenge chain on one key; current_agent_key() exposes key + thumbprint for ent#879. * services/aauth/config.py — aauth_person_server / AAUTH_PERSON_SERVER; keys.instance_seed(); .env.example AAuth block. * tests/unit/test_ent878_aauth_person_token.py — 105 tests on recorded Hellō shapes; the signed POST replays through verifier.verify_request. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… rows (abilityai/trinity-enterprise#878)
Checkpoint B of the person-token binding leg.
* routers/aauth_person.py (`# mcp: tools/aauth.ts`) —
POST /api/agents/{name}/aauth/person-token: uniform 404 unless AAuth is
live; the agent's OWN key only (403 aauth_agent_only — a human principal
may not make the backend sign as the agent); separate POST/poll/fleet
rate budgets; audit row on state change only (issued / pending / denied /
refused) under AuditEventType.CREDENTIALS with token_jti, execution_id
and trigger_source — never the token, handle, url or code; configuration
refusals as 409/422/502/503, protocol outcomes as 200 `refused`.
* models.py — PersonTokenRequest / PersonTokenInteraction /
PersonTokenResponse (no field can carry the token; pinned by test).
* main.py — router registered after the #623 discovery router.
* MCP: tools/aauth.ts `request_person_token(resource, pending_handle?,
wait?)` — self-acting, agent scope only, returns outcome + jti/aud/sub/exp/
cached + interaction{url, code, link, pending_handle, retry_after} +
next_step; description tells the agent to relay the link privately to
its OWNER (whoever approves first becomes the person it acts for) and to
use ask_operator when no person is in the conversation (T7). Always
registered under operatorOnly (T3): flag off → backend 404 → the tool
reports `aauth_disabled: true` — advertised but inert. access.ts policy
row, client.ts requestPersonToken, tool-visibility.test.ts case.
* tests: route tests (404 inert, 403 human/sibling/system, 409/422/503
named, issued+audited, pending→resume→issued, denial as outcome,
response field set, main.py wiring pin) + aauth.test.ts (self-acting
gate, passthrough, token-field strip, flag mapping); the new route is
classified in tests/unit/_route_census.py.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e dev merge (#3644) - integrations.py: the settings-change audit row carried `credential_cleared` twice after the dev merge; Python keeps the last key, so switching a bearer endpoint to `aauth` recorded `false` while the store dropped the secret. The ent#623 line stays; the stale dev line goes. Router-level test proves the row says `true` on the switch (red against the duplicate). - a2a.py: the three `request.url.path` reads become `request.scope["path"]` (#3102 fence); the signature base keeps `raw_path` first. - .gitleaksignore: fingerprint for the `private_key: Ed25519PrivateKey = field(repr=False)` dataclass field at keys.py:43 (generic-api-key false positive). The range scan re-flags the adding commit, so an inline `gitleaks:allow` on the head cannot clear it; the fingerprint can. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…on forwards the mapped scheme (#3644) - _route_census.py: the three ent#623 discovery documents (/.well-known/aauth-agent.json, -jwks.json, -resource.json) are OWN_AUTH — anonymous by design, public material only, uniform 404 while the prototype flag is off. The AAuth route class on POST /a2a/{name} is the same registered route the census already lists; nothing new to classify there. - nginx.conf: the /.well-known/aauth- location sent `X-Forwarded-Proto $scheme`, which is always http behind the TLS terminator (#3215). It now mirrors the /a2a/ block's headers (mapped $fwd_proto); the sibling count moves 3 → 4. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ettings are unreadable (#3644) (d) `test_3215_public_base_url` drives the real card route with a settings stand-in that knows only the public URL. The branch made dev's single card builder (ent#180) consult `aauth_config.active_issuer()`, whose flag read called `settings_service._resolve_bool_flag` unguarded, so the card 500ed and the four card/door tests that follow it failed. `is_enabled()` now treats an unreadable flag as OFF (AAuth inert, dev's bytes), the direction `get_issuer` already takes; regression test on the real card route. I1: two tests execute the merge-added ent#838 gate on the AAuth door — an internal-scope agent answers an outside AAuth caller with the keyed caller's uniform 404 (no fetch, no audit row), and admits a trusted one as its verified identity, never its address. I2: comment on `fetch_card_for` naming its overlap with ent#838's own-path-then-origin card order; code unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…it allowlists (#3644) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cedes body validation (abilityai/trinity-enterprise#878) Review F1 (HIGH): `signer.agent_token_info` reused a cached window-N key for a chain asking for more remaining life than the window had left, so the key retired at the boundary mid-chain (T6 ruling: a whole chain finishes on one key; ent#879 would pay an extra consent near window edges). A cache hit now satisfies both bounds a miss would — `now + min_remaining < window_end` as well as the expiry margin — pinned by a test that starts from a POPULATED cache (the existing test only covered an empty one, where the bounds coincide), including the exact boundary (the key retires AT window_end). Review I1: a 429 on the initial POST is a refusal with no handle, so its `next_step` no longer points at `interaction.retry_after` (`slow_down_new`). Review I2: when the refreshed record cannot be persisted, the answer itself carries the back-off (`status=slow_down` with the PS's interval) instead of relying on the un-saved early-poll guard; the 429 arm logs instead of `pass`. Review I3: `rate_limiter.redis_client()` is the public seam; `person_token` no longer reaches a sibling's private through a from-import alias (Invariant #1(b)). Review I4: `_require_live` is a `Depends` declared after `get_current_user`, so the flag gate runs BEFORE body validation — with the flag off a malformed body answers the uniform 404 instead of a 422 that reveals the route (test: flag off + four bad bodies → 404; live again → 422). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…range scan still sees (#3644) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(abilityai/trinity-enterprise#878) Checkpoint C of the person-token binding leg: - `scripts/dev/aauth_prototype/bind.sh <agent> <resource>` — binds one agent against the real person server with the agent's OWN MCP key (demo.sh's `agent_key()`), prints link + code for the owner, polls with the handle honouring `retry_after`, exits 0 on `issued` printing jti/aud/sub/exp — never the token (the response has no field for it). - `demo.sh`: `up`/`wire` set `aauth_person_server` (AAUTH_PERSON_SERVER, default the Hellō person server); runbook note + `rewire` warning: a new issuer host is a new agent to the PS, so do not rewire after binding. - Flow doc `a2a-aauth-agent-identity.md`: Flow 5 (binding), its refusal table, the ent#878 layers / surface / config rows, and the stated limits — binding hijack (relay only to your owner, privately; owner-only delivery is the productization fix), the instance key binds the instance's name for the agent, per-worker hold, PS signature not verified here, and the tool is advertised but inert while the flag is off (T3 deviation). - `requirements/mcp.md` §32.9 (FR-1..8 + out of scope / stated limits), chained after §32.8; ent#879 will be §32.10. - Catalog lines: `api-endpoints.md` (the route), `backend.md` (router + the four new `aauth/` modules and the HKDF signer change), `mcp-server.md` (`aauth.ts` / `request_person_token`); `feature-flows.md` index. - `tests/registry.json` entry for `test_ent878_aauth_person_token.py`. Debt entries (owner-only link delivery / T7, dynamic tool registration via the #846 reconciler / T3, RS256/ES256 verification of PS-issued tokens / #558 task G) are recorded in the private DEBT_INBOX. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…fication (abilityai/trinity-enterprise#879) Checkpoint A of the outbound AAuth resource client, stacked on ent#878. New under services/aauth/: - resource_meta.py — aauth-resource.json (issuer equality, same-origin jwks and control endpoints, access_mode, additional_signature_components) with the person_server fetch/cache discipline, and a tolerant AAuth-Budget parser. - resource_token.py — the security sink: every claim of a 401 auth-token challenge's resource token is checked against what WE presented (iss, dwk, aud == our PS, agent_jkt, ps, sub, presented_jti, timestamps, skew, lifetime, typ/alg/header params, kid → key under aauth-resource.json, signature) before the PS is contacted; one named check per failure. - auth_token.py — the exchange outcome types and the PS-answer inspection (T5: claims + cnf binding, no PS signature check). - resource_client.py — the ladder: token choice honouring access_mode, every 401/202/4xx transition (agent-token retry, person-token via ent#878, auth-token verify→exchange, step-up once, expired_jwt re-auth once, expired_presented fresh chain once, resource-side 202 → handle, never polled inline), bounds (≤6 sends, ≤3 exchanges, 25 s budget, possibly_executed on a POST timeout), and token scrubbing on every body/error. The Redis cache/pending/exchange seams are named and answer "unavailable" until checkpoint B wires ps_client. Additive edits to the stacked modules: - discovery: fetch_issuer_keys/resolve_key take dwk (cache keyed by (issuer, dwk)). - httpsig: components_for/sign accept extra_components (content-type/-digest only). - signer: sign_request(presented_token=, expected_key_thumbprint=, extra_components=) + KeyRotated; the agent key always signs. - egress.request_capped(peer=) so a resource-leg error names the resource. - person_token.forget(agent, resource) for the fresh-chain path. tests/unit/test_ent879_aauth_resource_client.py: 125 tests — one red per resource-token check, every ladder transition through the real signer → egress → MockTransport path with the scripted resource reading what was signed. ent#878 (115) and ent#623 (79) suites stay green. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…t, resume (abilityai/trinity-enterprise#879)
Checkpoint B of the outbound AAuth resource client.
- services/aauth/ps_client.py — the draft §Auth Token Endpoint leg: a POST to
the PS auth_token_endpoint signed with the AGENT token carrying
{resource_token, presented_token, justification, capabilities} + Prefer: wait;
the answer inspected (T5) and mapped to AuthTokenIssued / ExchangePending /
ExchangeRefused with the PS's own error vocabulary preserved. The auth-token
cache lives in Redis under aauth:at:{sha256(our_issuer|ps|agent|jkt|resource)}
(TTL to expiry, served only with ≥ 5 min left and the caller's key; unreadable
→ "not held"). Pending records (ps_interaction / ps_approval /
resource_polling) share ent#878's handle namespace through its now-public
save_record/load_record/delete_record; each tool answers pending_unknown for
the other's kinds; a record carries the stored request and never a token.
- services/aauth/resource_allowlist.py — the sink between an LLM-supplied URL
and a signed request: aauth_resource_allowlist (system_settings → env →
nothing), origin EQUALITY against strict server identifiers, and fail closed
on every unreadable state — unset, empty, malformed JSON, a non-list, or a
single bad entry empties the whole list.
- resource_client: the allow-list gate before any egress; seams wired to
ps_client; resume(agent, handle) — one signed GET of the pending URL (PS
exchange or resource-side deferral) and, when the PS mints the auth token,
the STORED request is re-sent (never caller-supplied args); terminal /
transient / slow-down / early-poll states; key_rotated and handle_unknown
fail closed.
- person_token (ent#878): signed_ps_request, save/load/delete_record and
DENIAL_CODES made public (no test pinned the privates); records carry
kind=person_token and resume() refuses other kinds.
- .env.example: AAUTH_RESOURCE_ALLOWLIST.
tests/unit/test_ent879_aauth_resource_client.py: 185 tests (A+B) — the
allow-list fail-closed matrix (empty, unreadable, malformed, origin mismatch,
prefix, port, path), the exchange mapping, the cache, the records, every
resume state, and the acceptance beat: the second call presents the cached
auth token with zero PS trips. ent#878 (115) and ent#623 (79) stay green.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…laration pattern; fingerprint signer.py:56 (abilityai/trinity-enterprise#878) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h-resource-client
… rows (abilityai/trinity-enterprise#879)
Checkpoint C — the auth boundary over the ent#879 ladder.
Route `POST /api/agents/{name}/aauth/resource-call` (routers/aauth_resource.py,
`# mcp: tools/aauth.ts`, registered after aauth_person): ent#878's own gates
reused, not copied — `require_live` as a Depends declared after
get_current_user (flag off → uniform 404 before body validation) and
`assert_own_key` (human / sibling / system key → 403 aauth_agent_only) BEFORE
the allow-list is consulted; the two helpers go public on aauth_person with a
`what=` message parameter. Discriminated request (models.py): a new call
(resource, method, path, body, justification, REQUIRED dedup_label) XOR a
resume (pending_handle) via a callable Discriminator; no request or response
field can carry a token (token_kind is a three-word Literal; pinned).
Protocol outcomes are 200 with `outcome`; refusals are mapped HTTP errors
(403 / 402 / 409 / 422 / 429 / 503 / 504, 502 default) carrying
possibly_executed on a POST timeout (#914).
Service seam services/aauth/resource_call.py: the ladder under the #1084
effect guard with identity {resource, method, path} + dedup_label (never the
body); only result / denied complete the claim, every other outcome leaves
via _NotTerminal so the claim is released and the post-consent retry runs
for real; a resume never enters the guard; in-flight → 409, unguarded on a
pull agent → 422 (#2392). Audit: EXECUTION aauth_resource_call after egress,
AUTHORIZATION aauth_resource_refused before it, details allow-listed — agent,
resource, method, path, token kind, outcome, status, budget cost, challenges;
never a token, the body, the justification, a handle, a url or a code.
Activity row direction aauth_resource (host only), fail-open.
MCP `call_aauth_resource(resource, method, path, body, justification,
dedup_label | pending_handle)` in tools/aauth.ts through the same self-acting
seam (agent scope only), always registered under operatorOnly (ent#879 T4,
the ent#878 mechanism); returns the backend union verbatim plus `agent`,
strips any token-shaped field, and turns an HTTP refusal into
{outcome: refusal, refusal{reason, message, http_status, retry_after?,
challenges?, possibly_executed?}, do_not_retry, next_step} naming the actor.
client.ts callAAuthResource, access.ts policy row, server.ts comment,
tool-visibility.test.ts both tools, aauth.test.ts cases; one census entry.
Tests: tests/unit/test_ent879_aauth_resource_route.py (19) over the real
ladder + TestClient — gate order, discriminated body, result / replay /
pending-release / resume / denied / expired / refusal mapping / 409 / 422 /
timeout, audit allow-list, activity shape, main.py order, catch-all resolve,
gate reuse; MCP aauth.test.ts 88 incl. description < 2048 chars.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…bilityai/trinity-enterprise#879) Checkpoint D — the live proof and the docs. demo.sh: step 7 `bind` (bind.sh <agent> https://senzing.aauth.dev — prints the Hellō link + code, waits for `issued`) and step 8 `sanctions check` (senzing_check.py through the agent's OWN key against POST /v1/search_entities {"FEATURES": [{"NAME_FULL": …}, {"ADDR_COUNTRY": …}]}: the identity used, the challenge sequence actually seen, the top candidates' match_key / match_level, the AAuth-Budget cost, then a second call that must show challenges: [] and token_kind: auth — zero PS trips — then A's aauth_resource_* audit rows). `rehearse` runs 1–8 with no pause (step 7 asserts the binding is present, step 8 prints a consent link instead of polling). `wire` sets aauth_person_server + aauth_resource_allowlist (Senzing + whoami, origin equality); `down` deletes both keys. Step 8 carries execution_id + a per-call dedup_label; DEMO_EXECUTION_ID defaults to `manual` and the header says why: the effect guard arms only for a real execution of the agent (the MCP path), an unknown id would be refused on a pull pilot (#2392). scripts/dev/aauth_prototype/senzing_check.py: stdlib-only, runs from a laptop with the agent's MCP key; handles interaction_required (link for the OWNER, polls honouring retry_after; --no-wait prints and exits 3), prints nothing that is a token. Docs: requirements/mcp.md §32.10 (FR-1..FR-9 + out of scope + stated limits, chained after §32.9); feature-flows/a2a-aauth-agent-identity.md Flow 6 (the ladder, nine steps + bounds), its refusal table, public-surface and configuration rows, stated limits 10–14 (allow-list is a settings key; auth tokens + pending records in Redis under the transient-secret posture; PS signature inspected not verified; four-party aud deferred; the guard arms only for a real execution), Related; catalog lines in architecture/{api-endpoints,mcp-server,backend}.md; tests/registry.json entries for test_ent879_aauth_resource_client.py, test_ent879_aauth_resource_route.py and mcp-server/src/tools/aauth.test.ts. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…etime bounds, path normalisation (abilityai/trinity-enterprise#879) - M1: ps_client.save_pending checks ent#878's per-agent open-interaction bound (MAX_PENDING_PER_AGENT, shared set) before writing; the ladder maps it to the same too_many_pending refusal person_token() returns. - M2: inspect_auth_token checks ps == our PS and caps exp - iat at 1 h. - L1: a control-endpoint path matches with one trailing slash stripped. - L2: ResourceToken.token is repr=False like its three siblings. - L3: the 25 s call budget starts at entry (covers discovery), both in call_resource and resume. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iew lows (abilityai/trinity-enterprise#879) - L4/L7: AAUTH_PROTOTYPE_ENABLED, AAUTH_ISSUER, AAUTH_PERSON_SERVER and AAUTH_RESOURCE_ALLOWLIST forwarded in backend.environment of docker-compose.yml and docker-compose.prod.yml (#1056 class), and mirrored into docker-compose.hosted.yml, which the #2280 parity guard requires to equal prod. - L6: a POST whose connection drops after the write (egress 'unreachable') reports possibly_executed, like a timeout (#914). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Integration surface for the AAuth stack #2956 → #3659 → #3667 (train is #3667's head merged onto current dev). Never merged; members merge individually once green.