Repository navigation
Conversation
Embedding programs can now decide every tool call (allow, notify, deny, or pending with a review ID) and receive every invocation change inside its database transaction. With a decider set, Atryum skips its rules, plans and default policy and mounts only the agent-facing routes. Every invocation state change is now written with its event in one transaction, and a human-gated MCP call waits on its database row instead of an in-process channel, so a decision recorded by another process releases it. Decisions are compare-and-set on status. Wake-up is still by polling; LISTEN/NOTIFY is the follow-up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Migrate applies built-in and extension migrations without starting the server, for an embedding program's separate migrate step. The environment variable lets such a program's other commands share one database setting with the server. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With a decider set, the MCP proxy no longer offers the atryum_rules and plan tools, annotates tools/list with Atryum rules, appends rules to denials, or returns initialize instructions that point at them: the embedding program's decider, not Atryum's rules table, governs the call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review fixes for the embedding extension points: - A decision that cannot be recorded before dispatch now denies the call instead of running it with no record. Denials whose record fails are logged loudly instead of dropped. - After a tool has run, its outcome is written with a context the caller's disconnect cannot cancel, and a record failure is logged, not returned, so the agent is not invited to retry a call that already ran. - A held call whose caller goes away re-reads its row before failing it, so a recorded approval is kept, and retries with backoff rather than leaving the row held with no waiter. - A decider panic is recovered and denies the call. Under a decider, held calls are not sent to the summarizer and plans are not matched. - WithRequireAgentAuth refuses to start without an enabled [[auth]] issuer or with skip_verify, and agent routes reject requests without a verified identity, so a caller-supplied agent_id never stands in for one. - WithDatabaseURL replaces the ATRYUM_DATABASE_URL override, which changed standalone behaviour. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Postgres checks CREATE on the schema even for CREATE TABLE IF NOT EXISTS on an existing table, so a least-privilege service role could not start Atryum. The migrations tables are now created only when missing; a pending migration still fails fast for such a role. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under WithRequireAgentAuth, an issuer or JWKS URL must be HTTPS; plain HTTP is allowed only on loopback, for local development. Keys fetched over plain HTTP could be swapped in transit. The summarizer-under-decider test now holds a call on the MCP proxy path (Invoke), where the leak was; it fails without the fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#168 enables atr_ agent API keys on the agent routes unconditionally. An embedder with WithDecider registers agents itself and would not know a key's agents.id, so keys are now off by default there (standalone keeps them on), and WithAgentAPIKeys overrides either way. With keys off, an atr_ bearer gets 401 on the agent routes even where an unrecognised bearer would otherwise pass (no IdP). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cachafla
force-pushed
the
aa/extension-points
branch
from
October 15, 2026 15:51
e479127 to
4e3d884
Compare
This branch has not been deployed
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.
What this adds
Embedding hooks that let another Go service run Atryum as its enforcement engine and own the decisions. Standalone Atryum (no options set) behaves as before.
WithDecider(d): every tool call on every path (MCP proxy, direct invoke, hookSubmit, the Managed Agents bridge) is handed tod, which returns allow, notify, deny or pending. With a decider set, Atryum's own approval rules, plans, default policy provider, operator routes and UI are not used, and the summarizer doesn't send tool arguments to Atryum's LLM backend. A decider error, unknown verdict or panic denies the call.WithEventHook(h):hruns inside the database transaction that changes an invocation, so an embedder can record events atomically with the state change. Invocation writes are now transactional (internal/store/tx.go). If the decision can't be recorded, the call is denied and never runs. Results recorded after execution use a context the client can't cancel, so a disconnect can't drop them.WithRequireAgentAuth(): refuses to start without an[[auth]]block, refusesauth_debug.skip_verify, rejects agent routes with no verified identity (401), ignores caller-suppliedagent_id, and requires HTTPS issuer and JWKS URLs except on loopback.WithDatabaseURL(url)andMigrate: let an embedder supply the database and run migrations, and start with a role that lacksCREATEonce the schema exists.docs/architecture.mdandCHANGELOG.mdare updated.Why
Agent Authority (ValidMind's commercial control plane) embeds Atryum through
pkg/atryumand needs to plug in its own decisions and append-only event record. The hooks are generic, so they live here.Tests
go test ./...passes. New tests cover the decider on each path, deny on decider error and panic, transaction rollback when the hook fails, fail-closed when a decision can't be recorded, results surviving a client disconnect, cancel-after-approve, required agent auth, and the summarizer under a decider on the MCP proxy path. Each was checked to fail without its fix.🤖 Generated with Claude Code
Rebased onto #168 (main 7518c28) — 15 October
Routesstill returns early with the agent routes only, ahead of Nibz/claude/api keys #168's operator middleware.runServerkeeps Nibz/claude/api keys #168's identity wiring next toSetAgentRoutesOnlyandUseExternalDecider.WithAgentAPIKeys(enabled bool)(4e3d884). Nibz/claude/api keys #168 acceptedatr_agent API keys on the agent routes unconditionally.WithDecider, whose embedder keeps its own agent registry.atr_bearer gets 401 on/mcp/,/api/v1/invocationsand/api/v1/external/*, even with no IdP configured, where an unknown bearer would otherwise be ignored.aa/pin-e479127.