@axtary/proxy
v0.6.1
Published
Local fail-closed runtime proxy for Axtary policy, ActionPass issuance, and ledger recording.
Downloads
1,145
Maintainers
Readme
@axtary/proxy
Local fail-closed runtime proxy for Axtary policy, ActionPass issuance, and ledger recording.
Early 0.x release: the runtime path is real and tested, but the API is not stable yet and may change between minor versions.
The source repository is currently private. Public product documentation and runnable guides are at axtary.com/docs.
npm install @axtary/proxyWhat It Does
- Parses a normalized Axtary action.
- Evaluates it through
@axtary/policy. - Rejects caller-supplied final provenance labels and can invoke a trusted
provenanceResolverbefore policy evaluation. - Carries authority-stamped field/source lineage through policy, ActionPass, handler input, and the hash-chained ledger.
- Requires ActionPass signing before execution by default.
- Can issue ActionPass v2 when the host supplies a stable status-reference allocator; v2 remains DPoP-bound and delegation-aware.
- Appends every executable or blocked decision to
@axtary/ledger. - Executes only registered handlers for allowed tools.
- Can resolve exact approval evidence for
step_updecisions before issuing a pass. - Atomically reserves trusted per-tool budget before pass issuance, commits on
success, rolls back on failure, and denies
budget_exceeded. - Returns structured blocked, executed, failed, and malformed results.
- Returns per-stage timings for parse, policy, budget reservation, ActionPass issuance, ledger append, handler execution, the completed authorization decision, and total latency.
- Accepts either a static policy object or an async policy getter for hot-reloaded local configs.
Quickstart
This example runs as-is with Node 20+:
import { createProxyRuntime } from "@axtary/proxy";
import { createFakeHandlers } from "@axtary/adapters";
import { LocalJsonlLedger } from "@axtary/ledger";
import { demoAction } from "@axtary/actionpass";
const proxy = createProxyRuntime({
issuer: "https://axtary.local",
tenant: "org:example",
ledger: new LocalJsonlLedger(".axtary/ledger.jsonl"),
handlers: createFakeHandlers(),
budget: {
enabled: true,
scope: "tenant",
limits: { actions: 100, externalMessages: 10 },
costs: {
"*": { actions: 1 },
"slack.chat.postMessage": { externalMessages: 1 },
},
},
allowUnsignedExecution: true, // local demo only; real runs sign every pass
});
// A PR inside policy with passing tests: decision, ledger write, then handler.
const safeAction = structuredClone(demoAction);
safeAction.capability.payload.testsPassed = true;
console.log((await proxy.handle(safeAction)).status); // executed
// The same PR without test evidence requires step-up; with no approval
// resolver attached, the proxy blocks before the handler is ever called.
console.log((await proxy.handle(demoAction)).status); // blockedDesign Notes
The proxy is the first runtime enforcement surface. It is intentionally local and deterministic. Real adapters register handlers and let the proxy own the decision, pass issuance, and ledger write before any production tool is touched.
The runtime package does not choose key custody or status transport. The CLI
host provides the persisted issuer keyring, public JWKS/status endpoints, and
status allocator; embedders may provide equivalent infrastructure through the
same signingKey, keyId, and statusReference seams.
Unsigned execution is possible only with allowUnsignedExecution: true, which is meant for dry-run tests and local demos.
Handlers are bounded by handlerTimeoutMs from config. Timeout failures are recorded as failed executions after the allow decision is ledgered, which keeps the authorization trace intact without letting slow providers stall the runtime.
When a dynamic policy getter fails, the proxy fails closed, records a deny decision in the ledger, and does not call the downstream handler. That preserves the audit trail while avoiding stale or partially parsed policy.
approvalResolver is the local bridge to hosted or local approval queues. It receives the normalized action and step_up policy decision, and may return a payload-bound approval artifact. If resolution fails, returns nothing, or the artifact does not bind to the exact action, the proxy blocks before handler execution.
provenanceResolver is an authority boundary, like the budget meter. Agent
actions cannot supply final integrity labels. A trusted host may attach exact
axtary.provenance.v0 field/source bindings before policy; resolver failures
fail closed as malformed input and produce no misleading decision record.
Budget limits and costs are trusted runtime configuration. Agent actions cannot supply reservation state. Pending reservations count against limits, committed usage is reconstructed from the local ledger after restart, and expired crash-era pending reservations are released during reconstruction. The meter records usage units only; it does not price or bill them.
Timing contract
ProxyTimings.decisionMs measures from receipt of a valid action through
parsing, deterministic policy evaluation, local budget reservation, ActionPass
issuance, and the crash-durable decision-ledger append. For authorize(),
totalMs equals decisionMs. For handle(), totalMs continues through the
provider handler and execution-outcome append, while decisionMs stops before
the handler is invoked.
The repository regression suite asserts that the p95 signed local
authorize() decision stays below
LOCAL_AUTHORIZATION_DECISION_BUDGET_MS (200ms) while appending to a seeded
10,000-record local ledger. Human approval waits, remote provider execution,
and optional remote dashboard sync are outside that deterministic local
decision budget.
