@axtary/approvals
v0.1.0
Published
Hosted approval queue contract for exact Axtary step-up approvals.
Maintainers
Readme
@axtary/approvals
Hosted approval queue contract for exact Axtary step-up approvals.
Early 0.x release: the runtime path is real and tested, but the API is not stable yet and may change between minor versions.
npm install @axtary/approvalsWhat It Does
- Creates pending approval queue items from normalized actions and
step_uppolicy decisions. - Stores exact action hashes and payload hashes for reviewer trust.
- Records request provenance so reviewers can tell whether a request came from the proxy, SDK, API, dashboard, or demo tooling.
- Carries optional provider diff metadata so reviewers can inspect changed files and hunks before approving.
- Approves queue items by producing
@axtary/actionpassapproval artifacts. - Denies queue items without producing executable approval evidence.
- Exposes a small in-memory queue implementation for API route tests and local demos.
- Persists hosted approval queue state to local JSON for development and self-hosted demos.
- Provides an HTTP client and resolver that local proxy runtimes can use to fetch approved artifacts.
Contract
The hosted control plane should coordinate approval state only. It should not hold downstream provider secrets or execute customer tool calls.
Local enforcement remains in @axtary/proxy. When a queued item is approved, the proxy can fetch the approval artifact and submit it through its approvalResolver; ActionPass issuance still validates that the artifact binds to the exact action and payload.
Queue items include provenance:
{
"provenance": {
"source": "proxy",
"createdBy": "agent:codex-prod",
"sourceId": "codex-cli",
"createdFrom": "axtary.proxy"
}
}Existing local JSON records without provenance parse as source: "api" for compatibility.
Queue items can also include provider-specific diff metadata:
{
"diff": {
"provider": "github",
"summary": "1 modified file, 2 additions, 1 deletion",
"files": [
{
"path": "infra/prod/main.tf",
"status": "modified",
"additions": 2,
"deletions": 1,
"hunks": [
{
"header": "@@ -12,3 +12,4 @@",
"lines": [
{ "type": "context", "oldLine": 12, "newLine": 12, "content": "..." },
{ "type": "deletion", "oldLine": 13, "content": "..." },
{ "type": "addition", "newLine": 13, "content": "..." }
]
}
]
}
]
}
}HTTP Shape
The Next app exposes the first local hosted API contract:
POST /api/approvalscreates a pending queue item from{ action, decision }.GET /api/approvals?status=pendinglists queued items.GET /api/approvals/:idreturns one queue item.POST /api/approvals/:id/approveresolves an item and returns an approval artifact.POST /api/approvals/:id/denydenies an item without returning approval evidence.POST /api/approvals/democreates a local dashboard exercise request withsource: "demo".
Only step_up decisions can enter the queue.
By default, the Next API routes persist local development queue state to .axtary/approvals.json, which is ignored by git. Set AXTARY_APPROVAL_STORE_PATH to choose a different JSON file, or AXTARY_APPROVAL_STORE_MODE=memory for ephemeral tests.
Proxy Resolver
createHostedApprovalResolver({ baseUrl }) returns a resolver compatible with the proxy approvalResolver shape:
import { createHostedApprovalResolver } from "@axtary/approvals";
const approvalResolver = createHostedApprovalResolver({
baseUrl: "https://app.axtary.dev",
requestedBy: "agent:codex-prod",
});The resolver creates or reuses a deterministic approval request ID based on the action hash. It returns null while the request is pending or denied, and returns an approval artifact only after the hosted queue has approved the exact action payload.
Quickstart
This example runs as-is with Node 20+:
import { InMemoryApprovalQueueStore } from "@axtary/approvals";
import { evaluatePolicy } from "@axtary/policy";
import { demoAction } from "@axtary/actionpass";
// A step_up decision becomes a reviewable queue item bound to the exact payload.
const store = new InMemoryApprovalQueueStore();
const item = await store.create({
action: demoAction,
decision: evaluatePolicy(demoAction),
requestedBy: "agent:codex-prod",
});
console.log(item.status, item.payloadHash);
// Approving produces a payload-bound approval artifact the proxy can execute with.
const approval = await store.approve({
id: item.id,
approvedBy: "user:[email protected]",
reason: "Reviewed the exact payload",
});
console.log(approval.item.status, approval.artifact.payloadHash === item.payloadHash);