@axtary/cli 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -8
- package/dist/index.d.ts +110 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +474 -12
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -8,10 +8,11 @@ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
|
8
8
|
import { ACTIONPASS_V2_VERSION, ACTIONPASS_V1_VERSION, analyzePostgresSelect, AxtaryDecisionSchema, FileDpopReplayStore, LocalActionPassStatusStore, LocalCaepMappingStore, LocalSsfReplayStore, LocalActionPassTrustStore, createApprovalArtifact, demoAction, hashPayload, issuerJwks, issuerSigningKey, issuerVerificationKeyResolver, explainDecision, loadOrCreateIssuerKeyring, parseNormalizedAction, processCaepSessionRevoked, rotateIssuerKeyring, signStatusListToken, } from "@axtary/actionpass";
|
|
9
9
|
import { createFakeAdapterState, createFakeHandlers, buildNativeConnectorDoctorPlans, createAwsRestHandlers, createActionPassVerifiedHandler, createGcpRestHandlers, createGitHubRestHandlers, createJiraRestHandlers, createLinearGraphqlHandlers, createLocalDocsHandlers, createPostgresNativeHandlers, createDriveRestHandlers, createSlackWebHandlers, GITHUB_NATIVE_CONNECTOR, githubAppRequiredEnv, mintGithubInstallationToken, resolveGithubAppCredentials, smokeAwsRestCredentials, smokeGcpRestCredentials, smokeGithubAppCredentials, smokeGitHubRestCredentials, smokeJiraRestCredentials, smokeLinearGraphqlCredentials, smokeLocalDocsRoots, smokePostgresCredentials, smokeDriveRestCredentials, smokeSlackWebCredentials, } from "@axtary/adapters";
|
|
10
10
|
import { loadAxtaryConfig, CachedConfigLoader } from "@axtary/config";
|
|
11
|
-
import { analyzeForensics, attestLedgerExport, buildLedgerAttestationBundle, exportLedgerRecords, formatLedgerExport, LedgerAttestationBundleSchema, LedgerExportFormatSchema, LocalJsonlLedger, OtlpHttpTraceExporter, proveLedgerConsistency, proveLedgerInclusion, syncLedgerExport, exportLedgerRecordsToOtlp, verifyLedgerAttestationBundle, verifyLedgerConsistencyProof, verifyLedgerInclusionProof, } from "@axtary/ledger";
|
|
11
|
+
import { analyzeForensics, attestLedgerExport, buildLedgerAttestationBundle, CROSS_ISSUER_TRUST_ROOT_VERSION, evaluateLedgerEquivalence, exportLedgerRecords, formatLedgerExport, LedgerAttestationBundleSchema, LedgerExportFormatSchema, LocalJsonlLedger, OtlpHttpTraceExporter, proveLedgerConsistency, proveLedgerInclusion, syncLedgerExport, exportLedgerRecordsToOtlp, verifyLedgerAttestationBundle, verifyLedgerConsistencyProof, verifyLedgerInclusionProof, } from "@axtary/ledger";
|
|
12
12
|
import { MCP_TOOL_CALL, McpHttpClient, McpStdioClient, createMcpAction, createMcpFixtureToolHandler, createMcpToolDefinition, createMcpToolRouter, createMcpWrapperServer, createMcpWrapperTool, evaluateMcpVersionChain, findMcpSignedDefinition, findMcpToolPin, loadMcpConformanceRegistry, loadMcpPinManifest, loadMcpPublisherTrustStore, loadMcpSignedDefinitionRegistry, McpPublisherKeySchema, mcpPublisherKeyResolver, pinMcpDefinitions, reconcileMcpPins, saveMcpConformanceRegistry, saveMcpPinManifest, saveMcpPublisherTrustStore, trustMcpPublisher, upsertMcpConformanceReceipt, verifyMcpSignedDefinition, } from "@axtary/mcp";
|
|
13
13
|
import { createPollingHostedApprovalResolver } from "@axtary/approvals";
|
|
14
14
|
import { applyPolicyTemplate, evaluatePolicy, explainPolicyDecision, getPolicyTemplate, getPolicyTemplates, runPolicyFixtures as runPolicyTemplateFixtures, simulatePolicy, } from "@axtary/policy";
|
|
15
|
+
import { actionFromAcsToolCallSnapshot, createAxtaryAcsManifest, evaluateAcsIntervention, } from "@axtary/policy/acs";
|
|
15
16
|
import { createProxyRuntime, } from "@axtary/proxy";
|
|
16
17
|
import { runClaudeCodeHook } from "./claude-code-hook.js";
|
|
17
18
|
import { runCodexHook } from "./codex-hook.js";
|
|
@@ -37,6 +38,7 @@ export const LEDGER_OTEL_EXPORT_RUN_SCHEMA_VERSION = "axtary.ledger_otel_export_
|
|
|
37
38
|
export const LEDGER_ATTEST_RUN_SCHEMA_VERSION = "axtary.ledger_attest_run.v0";
|
|
38
39
|
export const LEDGER_VERIFY_EXPORT_RUN_SCHEMA_VERSION = "axtary.ledger_verify_export_run.v0";
|
|
39
40
|
export const LEDGER_PROVE_INCLUSION_RUN_SCHEMA_VERSION = "axtary.ledger_prove_inclusion_run.v0";
|
|
41
|
+
export const LEDGER_PROVE_EQUIVALENCE_RUN_SCHEMA_VERSION = "axtary.ledger_prove_equivalence_run.v0";
|
|
40
42
|
export const LEDGER_PROVE_CONSISTENCY_RUN_SCHEMA_VERSION = "axtary.ledger_prove_consistency_run.v0";
|
|
41
43
|
export const LEDGER_VERIFY_PROOF_RUN_SCHEMA_VERSION = "axtary.ledger_verify_proof_run.v0";
|
|
42
44
|
export const LEDGER_FORENSICS_RUN_SCHEMA_VERSION = "axtary.ledger_forensics_run.v0";
|
|
@@ -95,6 +97,7 @@ export function authorizeDecisionHints(decision, policy) {
|
|
|
95
97
|
return hints;
|
|
96
98
|
}
|
|
97
99
|
export const POLICY_PARITY_SCHEMA_VERSION = "axtary.policy_parity.v0";
|
|
100
|
+
export const ACS_CONFORMANCE_SCHEMA_VERSION = "axtary.acs_conformance.v0";
|
|
98
101
|
const MCP_DEMO_TOOL_DEFINITION = createMcpToolDefinition({
|
|
99
102
|
serverIdentity: "mcp:axtary-demo-fixtures",
|
|
100
103
|
serverVersion: "1.0.0",
|
|
@@ -295,6 +298,9 @@ export async function startProxyServer(input = {}) {
|
|
|
295
298
|
endpoint: input.otlpEndpoint,
|
|
296
299
|
serviceName: input.otlpServiceName ?? "axtary-local-proxy",
|
|
297
300
|
timeoutMs: input.otlpTimeoutMs,
|
|
301
|
+
// The ledger library no longer falls back to ambient fetch; the
|
|
302
|
+
// CLI is the app boundary that supplies the transport.
|
|
303
|
+
fetch: globalThis.fetch,
|
|
298
304
|
}),
|
|
299
305
|
}
|
|
300
306
|
: {});
|
|
@@ -375,6 +381,20 @@ export async function startProxyServer(input = {}) {
|
|
|
375
381
|
writeJson(response, 200, issuerJwks(keyring));
|
|
376
382
|
return;
|
|
377
383
|
}
|
|
384
|
+
if (request.method === "GET" &&
|
|
385
|
+
request.url === "/.well-known/axtary-trust-root.json") {
|
|
386
|
+
writeJson(response, 200, {
|
|
387
|
+
schemaVersion: CROSS_ISSUER_TRUST_ROOT_VERSION,
|
|
388
|
+
issuer: loadedConfig.config.issuer,
|
|
389
|
+
jwks: issuerJwks(keyring),
|
|
390
|
+
statusListUri,
|
|
391
|
+
federationProfile: {
|
|
392
|
+
profile: "axtary.openid-federation-anchor.v0",
|
|
393
|
+
entityStatementTyp: "entity-statement+jwt",
|
|
394
|
+
},
|
|
395
|
+
});
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
378
398
|
if (request.method === "GET" &&
|
|
379
399
|
request.url === "/statuslists/actionpasses") {
|
|
380
400
|
const signed = await signStatusListToken({
|
|
@@ -1000,7 +1020,11 @@ async function connectMcpUpstream(target) {
|
|
|
1000
1020
|
});
|
|
1001
1021
|
headers = { ...headers, authorization };
|
|
1002
1022
|
}
|
|
1003
|
-
client = await McpHttpClient.start({
|
|
1023
|
+
client = await McpHttpClient.start({
|
|
1024
|
+
url: target.wrapUrl,
|
|
1025
|
+
headers,
|
|
1026
|
+
fetch: globalThis.fetch,
|
|
1027
|
+
});
|
|
1004
1028
|
}
|
|
1005
1029
|
else if (target.wrap) {
|
|
1006
1030
|
const [command, ...commandArgs] = target.wrap.split(/\s+/).filter(Boolean);
|
|
@@ -1899,6 +1923,17 @@ export async function runDemo(input = {}) {
|
|
|
1899
1923
|
verificationKey: publicKey,
|
|
1900
1924
|
}),
|
|
1901
1925
|
},
|
|
1926
|
+
// Same exact-payload approval primitive as the real workflows: the
|
|
1927
|
+
// artifact binds the payload hash, and the proxy stamps the
|
|
1928
|
+
// approved/executed hash pair on the execution record.
|
|
1929
|
+
approvalResolver: input.approveStepUp
|
|
1930
|
+
? async ({ action }) => createApprovalArtifact({
|
|
1931
|
+
action,
|
|
1932
|
+
mode: "human",
|
|
1933
|
+
approvedBy: input.approvedBy ?? "user:local-reviewer",
|
|
1934
|
+
reason: "Local exact approval for the demo step-up action.",
|
|
1935
|
+
}).artifact
|
|
1936
|
+
: undefined,
|
|
1902
1937
|
});
|
|
1903
1938
|
const results = [];
|
|
1904
1939
|
for (const demo of DEMO_ACTIONS) {
|
|
@@ -2591,18 +2626,172 @@ export async function runPolicyParity(input = {}) {
|
|
|
2591
2626
|
results.push({ id: fixture.id, passed, expected, engines });
|
|
2592
2627
|
}
|
|
2593
2628
|
const failed = results.filter((result) => !result.passed).length;
|
|
2629
|
+
const agentCore = parity.buildAgentCoreCedarExport({
|
|
2630
|
+
fixtures: fixturesFile.fixtures.map((fixture) => ({
|
|
2631
|
+
id: fixture.id,
|
|
2632
|
+
action: fixture.action,
|
|
2633
|
+
})),
|
|
2634
|
+
policyVersion: policyConfig.version,
|
|
2635
|
+
});
|
|
2594
2636
|
return {
|
|
2595
2637
|
schemaVersion: POLICY_PARITY_SCHEMA_VERSION,
|
|
2596
2638
|
engines: ["native", "cedar", "rego"],
|
|
2597
2639
|
fixturesPath,
|
|
2598
2640
|
cedarPath,
|
|
2599
2641
|
regoWasmPath,
|
|
2642
|
+
agentCore: {
|
|
2643
|
+
schemaVersion: agentCore.schemaVersion,
|
|
2644
|
+
status: agentCore.status,
|
|
2645
|
+
gatewayResource: agentCore.gatewayResource,
|
|
2646
|
+
fixtureCount: agentCore.fixtures.length,
|
|
2647
|
+
sample: (agentCore.fixtures[0] ?? null),
|
|
2648
|
+
},
|
|
2600
2649
|
passed: failed === 0,
|
|
2601
2650
|
total: results.length,
|
|
2602
2651
|
failed,
|
|
2603
2652
|
results,
|
|
2604
2653
|
};
|
|
2605
2654
|
}
|
|
2655
|
+
export async function runAcsConformance(input = {}) {
|
|
2656
|
+
const loadedConfig = await loadAxtaryConfig({
|
|
2657
|
+
filePath: input.configPath,
|
|
2658
|
+
cwd: input.cwd,
|
|
2659
|
+
});
|
|
2660
|
+
const cwd = resolve(input.cwd ?? process.cwd());
|
|
2661
|
+
const ledgerPath = input.ledgerPath
|
|
2662
|
+
? resolve(cwd, input.ledgerPath)
|
|
2663
|
+
: resolve(cwd, ".axtary/acs-conformance.jsonl");
|
|
2664
|
+
const ledger = new LocalJsonlLedger(ledgerPath);
|
|
2665
|
+
const { privateKey } = generateKeyPairSync("ec", { namedCurve: "P-256" });
|
|
2666
|
+
const manifest = createAxtaryAcsManifest();
|
|
2667
|
+
const runtime = createProxyRuntime({
|
|
2668
|
+
issuer: loadedConfig.config.issuer,
|
|
2669
|
+
tenant: loadedConfig.config.tenant,
|
|
2670
|
+
ledger,
|
|
2671
|
+
policy: loadedConfig.config.policy,
|
|
2672
|
+
signingKey: privateKey,
|
|
2673
|
+
keyId: "local-acs-conformance-key",
|
|
2674
|
+
allowUnsignedExecution: false,
|
|
2675
|
+
handlerTimeoutMs: loadedConfig.config.runtime.handlerTimeoutMs,
|
|
2676
|
+
budget: loadedConfig.config.runtime.budget,
|
|
2677
|
+
handlers: {
|
|
2678
|
+
"slack.chat.postMessage": async () => ({
|
|
2679
|
+
ok: true,
|
|
2680
|
+
provider: "acs-fixture",
|
|
2681
|
+
}),
|
|
2682
|
+
},
|
|
2683
|
+
});
|
|
2684
|
+
const vectors = [
|
|
2685
|
+
{
|
|
2686
|
+
id: "pre-tool-call-allow",
|
|
2687
|
+
interventionPoint: "pre_tool_call",
|
|
2688
|
+
snapshot: acsSlackSnapshot({
|
|
2689
|
+
channel: "#axtary-dev",
|
|
2690
|
+
message: "M9.4 ACS conformance allow",
|
|
2691
|
+
}),
|
|
2692
|
+
expectedVerdict: "allow",
|
|
2693
|
+
expectedProxyStatus: "executed",
|
|
2694
|
+
mode: "handle",
|
|
2695
|
+
},
|
|
2696
|
+
{
|
|
2697
|
+
id: "pre-tool-call-deny",
|
|
2698
|
+
interventionPoint: "pre_tool_call",
|
|
2699
|
+
snapshot: acsSlackSnapshot({
|
|
2700
|
+
channel: "#not-allowed",
|
|
2701
|
+
message: "M9.4 ACS conformance deny",
|
|
2702
|
+
}),
|
|
2703
|
+
expectedVerdict: "deny",
|
|
2704
|
+
expectedProxyStatus: "blocked",
|
|
2705
|
+
mode: "handle",
|
|
2706
|
+
},
|
|
2707
|
+
{
|
|
2708
|
+
id: "pre-tool-call-step-up",
|
|
2709
|
+
interventionPoint: "pre_tool_call",
|
|
2710
|
+
snapshot: acsSlackSnapshot({
|
|
2711
|
+
channel: "#axtary-dev",
|
|
2712
|
+
message: "M9.4 ACS conformance step-up",
|
|
2713
|
+
externalRecipients: ["customer@example.com"],
|
|
2714
|
+
}),
|
|
2715
|
+
expectedVerdict: "escalate",
|
|
2716
|
+
expectedProxyStatus: "step_up",
|
|
2717
|
+
mode: "authorize",
|
|
2718
|
+
},
|
|
2719
|
+
];
|
|
2720
|
+
const results = [];
|
|
2721
|
+
for (const vector of vectors) {
|
|
2722
|
+
const acs = evaluateAcsIntervention({
|
|
2723
|
+
manifest,
|
|
2724
|
+
interventionPoint: vector.interventionPoint,
|
|
2725
|
+
snapshot: vector.snapshot,
|
|
2726
|
+
config: loadedConfig.config.policy,
|
|
2727
|
+
});
|
|
2728
|
+
const action = actionFromAcsToolCallSnapshot(vector.snapshot);
|
|
2729
|
+
const proxy = vector.mode === "authorize"
|
|
2730
|
+
? await runtime.authorize(action)
|
|
2731
|
+
: await runtime.handle(action);
|
|
2732
|
+
const proxyStatus = "status" in proxy ? proxy.status : null;
|
|
2733
|
+
const ledgerLine = "ledger" in proxy ? proxy.ledger.lineNumber : null;
|
|
2734
|
+
const executionLedgerLine = "executionLedger" in proxy ? proxy.executionLedger.lineNumber : null;
|
|
2735
|
+
const actionPassId = "actionPass" in proxy && proxy.actionPass
|
|
2736
|
+
? proxy.actionPass.claims.jti
|
|
2737
|
+
: null;
|
|
2738
|
+
const passed = acs.verdict === vector.expectedVerdict &&
|
|
2739
|
+
proxyStatus === vector.expectedProxyStatus &&
|
|
2740
|
+
(vector.expectedProxyStatus !== "executed" || actionPassId !== null) &&
|
|
2741
|
+
ledgerLine !== null;
|
|
2742
|
+
results.push({
|
|
2743
|
+
id: vector.id,
|
|
2744
|
+
interventionPoint: vector.interventionPoint,
|
|
2745
|
+
expectedVerdict: vector.expectedVerdict,
|
|
2746
|
+
actualVerdict: acs.verdict,
|
|
2747
|
+
expectedProxyStatus: vector.expectedProxyStatus,
|
|
2748
|
+
actualProxyStatus: proxyStatus,
|
|
2749
|
+
passed,
|
|
2750
|
+
reasons: acs.reasons,
|
|
2751
|
+
actionHash: acs.evidence.actionHash ?? null,
|
|
2752
|
+
payloadHash: acs.evidence.payloadHash ?? null,
|
|
2753
|
+
actionPassId,
|
|
2754
|
+
ledgerLine,
|
|
2755
|
+
executionLedgerLine,
|
|
2756
|
+
});
|
|
2757
|
+
}
|
|
2758
|
+
const failed = results.filter((result) => !result.passed).length;
|
|
2759
|
+
return {
|
|
2760
|
+
schemaVersion: ACS_CONFORMANCE_SCHEMA_VERSION,
|
|
2761
|
+
manifestVersion: manifest.agent_control_specification_version,
|
|
2762
|
+
ledgerPath,
|
|
2763
|
+
passed: failed === 0,
|
|
2764
|
+
total: results.length,
|
|
2765
|
+
failed,
|
|
2766
|
+
vectors: results,
|
|
2767
|
+
};
|
|
2768
|
+
}
|
|
2769
|
+
function acsSlackSnapshot(input) {
|
|
2770
|
+
return {
|
|
2771
|
+
schemaVersion: "axtary.acs_snapshot.v0",
|
|
2772
|
+
actor: {
|
|
2773
|
+
agentId: "agent:codex-prod",
|
|
2774
|
+
humanOwner: "user:asrar@company.com",
|
|
2775
|
+
runtime: "codex-cli",
|
|
2776
|
+
tenant: "org:axtary",
|
|
2777
|
+
},
|
|
2778
|
+
intent: {
|
|
2779
|
+
taskId: "AXT-M9-4",
|
|
2780
|
+
declaredGoal: "Verify ACS pre_tool_call routing through Axtary",
|
|
2781
|
+
},
|
|
2782
|
+
tool_call: {
|
|
2783
|
+
tool: "slack.chat.postMessage",
|
|
2784
|
+
resource: "slack:workspace/company",
|
|
2785
|
+
arguments: {
|
|
2786
|
+
channel: input.channel,
|
|
2787
|
+
message: input.message,
|
|
2788
|
+
...(input.externalRecipients
|
|
2789
|
+
? { externalRecipients: input.externalRecipients }
|
|
2790
|
+
: {}),
|
|
2791
|
+
},
|
|
2792
|
+
},
|
|
2793
|
+
};
|
|
2794
|
+
}
|
|
2606
2795
|
export async function runLedgerExport(input = {}) {
|
|
2607
2796
|
const loadedConfig = await loadAxtaryConfig({
|
|
2608
2797
|
filePath: input.configPath,
|
|
@@ -2660,7 +2849,7 @@ export async function runLedgerSync(input) {
|
|
|
2660
2849
|
tenant: input.tenant ?? loadedConfig.config.tenant,
|
|
2661
2850
|
token,
|
|
2662
2851
|
export: exported,
|
|
2663
|
-
fetch: input.fetch,
|
|
2852
|
+
fetch: input.fetch ?? globalThis.fetch,
|
|
2664
2853
|
});
|
|
2665
2854
|
return {
|
|
2666
2855
|
schemaVersion: LEDGER_SYNC_RUN_SCHEMA_VERSION,
|
|
@@ -2690,7 +2879,7 @@ export async function runLedgerOtelExport(input) {
|
|
|
2690
2879
|
serviceName: input.serviceName ?? "axtary-ledger-export",
|
|
2691
2880
|
timeoutMs: input.timeoutMs,
|
|
2692
2881
|
records: exported.records,
|
|
2693
|
-
fetch: input.fetch,
|
|
2882
|
+
fetch: input.fetch ?? globalThis.fetch,
|
|
2694
2883
|
});
|
|
2695
2884
|
return {
|
|
2696
2885
|
schemaVersion: LEDGER_OTEL_EXPORT_RUN_SCHEMA_VERSION,
|
|
@@ -2819,13 +3008,56 @@ export async function runLedgerVerifyExport(input) {
|
|
|
2819
3008
|
const cwd = input.cwd ?? process.cwd();
|
|
2820
3009
|
const bundlePath = resolve(cwd, input.bundlePath);
|
|
2821
3010
|
const text = await readFile(bundlePath, "utf8");
|
|
2822
|
-
const
|
|
3011
|
+
const parsedBundle = JSON.parse(text);
|
|
3012
|
+
const result = await verifyLedgerAttestationBundle(parsedBundle, {
|
|
2823
3013
|
issuer: input.issuer,
|
|
2824
3014
|
});
|
|
3015
|
+
const equivalence = result.valid
|
|
3016
|
+
? evaluateLedgerEquivalence(LedgerAttestationBundleSchema.parse(parsedBundle).export.records)
|
|
3017
|
+
: null;
|
|
2825
3018
|
return {
|
|
2826
3019
|
schemaVersion: LEDGER_VERIFY_EXPORT_RUN_SCHEMA_VERSION,
|
|
2827
3020
|
bundlePath,
|
|
2828
3021
|
result,
|
|
3022
|
+
equivalence,
|
|
3023
|
+
};
|
|
3024
|
+
}
|
|
3025
|
+
/**
|
|
3026
|
+
* `axtary prove-equivalence` — the queryable, exportable approval↔execution
|
|
3027
|
+
* equivalence proof. Reads the ledger through the fail-closed export path
|
|
3028
|
+
* (a tampered chain refuses to export at all), then reports, per execution
|
|
3029
|
+
* record, the approved payload hash, the executed payload hash, and whether
|
|
3030
|
+
* they are equal. Executions without approval evidence report `unproven`,
|
|
3031
|
+
* never fake-proved.
|
|
3032
|
+
*/
|
|
3033
|
+
export async function runLedgerProveEquivalence(input = {}) {
|
|
3034
|
+
const loadedConfig = await loadAxtaryConfig({
|
|
3035
|
+
filePath: input.configPath,
|
|
3036
|
+
cwd: input.cwd,
|
|
3037
|
+
});
|
|
3038
|
+
const cwd = input.cwd ?? process.cwd();
|
|
3039
|
+
const ledgerPath = resolveConfiguredLedgerPath(input, loadedConfig);
|
|
3040
|
+
const exported = await exportLedgerRecords({
|
|
3041
|
+
filePath: ledgerPath,
|
|
3042
|
+
from: input.from,
|
|
3043
|
+
to: input.to,
|
|
3044
|
+
decisions: input.decisions,
|
|
3045
|
+
});
|
|
3046
|
+
const report = evaluateLedgerEquivalence(exported.records);
|
|
3047
|
+
const outputPath = input.outputPath ? resolve(cwd, input.outputPath) : null;
|
|
3048
|
+
if (outputPath) {
|
|
3049
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
3050
|
+
await writeFile(outputPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
|
|
3051
|
+
}
|
|
3052
|
+
return {
|
|
3053
|
+
schemaVersion: LEDGER_PROVE_EQUIVALENCE_RUN_SCHEMA_VERSION,
|
|
3054
|
+
config: {
|
|
3055
|
+
filePath: loadedConfig.filePath,
|
|
3056
|
+
tenant: loadedConfig.config.tenant,
|
|
3057
|
+
},
|
|
3058
|
+
ledgerPath,
|
|
3059
|
+
outputPath,
|
|
3060
|
+
report,
|
|
2829
3061
|
};
|
|
2830
3062
|
}
|
|
2831
3063
|
async function readAttestationBundle(cwd, bundlePath) {
|
|
@@ -3374,6 +3606,7 @@ export async function runRealWorkflow(input = {}) {
|
|
|
3374
3606
|
mutation: tamperSetup.mutation,
|
|
3375
3607
|
approvedPayloadHash: tamperSetup.approvedPayloadHash,
|
|
3376
3608
|
attemptedPayloadHash: tamperSetup.attemptedPayloadHash,
|
|
3609
|
+
scopedAuthority: tamperSetup.scopedAuthority,
|
|
3377
3610
|
reached: tamperStep !== null,
|
|
3378
3611
|
blocked: tamperStep?.status === "blocked",
|
|
3379
3612
|
blockReasons: tamperStep?.reasons ?? [],
|
|
@@ -4046,6 +4279,8 @@ export async function runAxtaryCli(argv = process.argv.slice(2), io = {}) {
|
|
|
4046
4279
|
configPath: parsed.configPath,
|
|
4047
4280
|
ledgerPath: parsed.ledgerPath,
|
|
4048
4281
|
cwd: io.cwd ?? parsed.cwd,
|
|
4282
|
+
approveStepUp: parsed.approveStepUp,
|
|
4283
|
+
approvedBy: parsed.approvedBy,
|
|
4049
4284
|
});
|
|
4050
4285
|
if (parsed.json) {
|
|
4051
4286
|
stdout(`${JSON.stringify(result, null, 2)}\n`);
|
|
@@ -4230,6 +4465,25 @@ export async function runAxtaryCli(argv = process.argv.slice(2), io = {}) {
|
|
|
4230
4465
|
}
|
|
4231
4466
|
return result.result.valid ? 0 : 1;
|
|
4232
4467
|
}
|
|
4468
|
+
if (command === "prove-equivalence") {
|
|
4469
|
+
const parsed = parseProveEquivalenceArgs(args);
|
|
4470
|
+
const result = await runLedgerProveEquivalence({
|
|
4471
|
+
configPath: parsed.configPath,
|
|
4472
|
+
ledgerPath: parsed.ledgerPath,
|
|
4473
|
+
cwd: io.cwd ?? parsed.cwd,
|
|
4474
|
+
from: parsed.from,
|
|
4475
|
+
to: parsed.to,
|
|
4476
|
+
decisions: parsed.decisions,
|
|
4477
|
+
outputPath: parsed.outputPath,
|
|
4478
|
+
});
|
|
4479
|
+
if (parsed.json) {
|
|
4480
|
+
stdout(`${JSON.stringify(result, null, 2)}\n`);
|
|
4481
|
+
}
|
|
4482
|
+
else {
|
|
4483
|
+
stdout(formatProveEquivalenceResult(result));
|
|
4484
|
+
}
|
|
4485
|
+
return result.report.failed === 0 ? 0 : 1;
|
|
4486
|
+
}
|
|
4233
4487
|
if (command === "prove-inclusion") {
|
|
4234
4488
|
const parsed = parseProveInclusionArgs(args);
|
|
4235
4489
|
const result = await runLedgerProveInclusion({
|
|
@@ -4711,6 +4965,21 @@ export async function runAxtaryCli(argv = process.argv.slice(2), io = {}) {
|
|
|
4711
4965
|
}
|
|
4712
4966
|
return result.passed ? 0 : 1;
|
|
4713
4967
|
}
|
|
4968
|
+
if (command === "acs") {
|
|
4969
|
+
const parsed = parseAcsArgs(args);
|
|
4970
|
+
const result = await runAcsConformance({
|
|
4971
|
+
configPath: parsed.configPath,
|
|
4972
|
+
ledgerPath: parsed.ledgerPath,
|
|
4973
|
+
cwd: io.cwd ?? parsed.cwd,
|
|
4974
|
+
});
|
|
4975
|
+
if (parsed.json) {
|
|
4976
|
+
stdout(`${JSON.stringify(result, null, 2)}\n`);
|
|
4977
|
+
}
|
|
4978
|
+
else {
|
|
4979
|
+
stdout(formatAcsConformanceResult(result));
|
|
4980
|
+
}
|
|
4981
|
+
return result.passed ? 0 : 1;
|
|
4982
|
+
}
|
|
4714
4983
|
if (command === "connect") {
|
|
4715
4984
|
const parsed = parseConnectArgs(args);
|
|
4716
4985
|
const result = await runConnect({
|
|
@@ -5627,6 +5896,39 @@ function parseTestPolicyArgs(args) {
|
|
|
5627
5896
|
}
|
|
5628
5897
|
return parsed;
|
|
5629
5898
|
}
|
|
5899
|
+
function parseAcsArgs(args) {
|
|
5900
|
+
const subcommand = args[0];
|
|
5901
|
+
if (subcommand !== "conformance") {
|
|
5902
|
+
throw new Error("acs_requires_conformance");
|
|
5903
|
+
}
|
|
5904
|
+
const parsed = {
|
|
5905
|
+
subcommand,
|
|
5906
|
+
json: false,
|
|
5907
|
+
};
|
|
5908
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
5909
|
+
const arg = args[index];
|
|
5910
|
+
switch (arg) {
|
|
5911
|
+
case "--config":
|
|
5912
|
+
parsed.configPath = requireValue(args, index, "--config");
|
|
5913
|
+
index += 1;
|
|
5914
|
+
break;
|
|
5915
|
+
case "--ledger":
|
|
5916
|
+
parsed.ledgerPath = requireValue(args, index, "--ledger");
|
|
5917
|
+
index += 1;
|
|
5918
|
+
break;
|
|
5919
|
+
case "--cwd":
|
|
5920
|
+
parsed.cwd = requireValue(args, index, "--cwd");
|
|
5921
|
+
index += 1;
|
|
5922
|
+
break;
|
|
5923
|
+
case "--json":
|
|
5924
|
+
parsed.json = true;
|
|
5925
|
+
break;
|
|
5926
|
+
default:
|
|
5927
|
+
throw new Error(`unknown_acs_flag:${arg}`);
|
|
5928
|
+
}
|
|
5929
|
+
}
|
|
5930
|
+
return parsed;
|
|
5931
|
+
}
|
|
5630
5932
|
function parseSmokeArgs(args) {
|
|
5631
5933
|
const parsed = {};
|
|
5632
5934
|
for (let index = 0; index < args.length; index += 1) {
|
|
@@ -6000,6 +6302,51 @@ function parseLedgerAttestArgs(args) {
|
|
|
6000
6302
|
}
|
|
6001
6303
|
return parsed;
|
|
6002
6304
|
}
|
|
6305
|
+
function parseProveEquivalenceArgs(args) {
|
|
6306
|
+
const parsed = {
|
|
6307
|
+
json: false,
|
|
6308
|
+
decisions: [],
|
|
6309
|
+
};
|
|
6310
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
6311
|
+
const arg = args[index];
|
|
6312
|
+
switch (arg) {
|
|
6313
|
+
case "--config":
|
|
6314
|
+
parsed.configPath = requireValue(args, index, "--config");
|
|
6315
|
+
index += 1;
|
|
6316
|
+
break;
|
|
6317
|
+
case "--ledger":
|
|
6318
|
+
parsed.ledgerPath = requireValue(args, index, "--ledger");
|
|
6319
|
+
index += 1;
|
|
6320
|
+
break;
|
|
6321
|
+
case "--cwd":
|
|
6322
|
+
parsed.cwd = requireValue(args, index, "--cwd");
|
|
6323
|
+
index += 1;
|
|
6324
|
+
break;
|
|
6325
|
+
case "--from":
|
|
6326
|
+
parsed.from = requireValue(args, index, "--from");
|
|
6327
|
+
index += 1;
|
|
6328
|
+
break;
|
|
6329
|
+
case "--to":
|
|
6330
|
+
parsed.to = requireValue(args, index, "--to");
|
|
6331
|
+
index += 1;
|
|
6332
|
+
break;
|
|
6333
|
+
case "--decision":
|
|
6334
|
+
parsed.decisions.push(parseDecisionFlag(requireValue(args, index, "--decision")));
|
|
6335
|
+
index += 1;
|
|
6336
|
+
break;
|
|
6337
|
+
case "--out":
|
|
6338
|
+
parsed.outputPath = requireValue(args, index, "--out");
|
|
6339
|
+
index += 1;
|
|
6340
|
+
break;
|
|
6341
|
+
case "--json":
|
|
6342
|
+
parsed.json = true;
|
|
6343
|
+
break;
|
|
6344
|
+
default:
|
|
6345
|
+
throw new Error(`unknown_prove_equivalence_flag:${arg}`);
|
|
6346
|
+
}
|
|
6347
|
+
}
|
|
6348
|
+
return parsed;
|
|
6349
|
+
}
|
|
6003
6350
|
function parseVerifyExportArgs(args) {
|
|
6004
6351
|
const parsed = { bundlePath: "", json: false };
|
|
6005
6352
|
for (let index = 0; index < args.length; index += 1) {
|
|
@@ -6328,6 +6675,13 @@ function parseDemoArgs(args) {
|
|
|
6328
6675
|
parsed.templateId = requireValue(args, index, "--template");
|
|
6329
6676
|
index += 1;
|
|
6330
6677
|
break;
|
|
6678
|
+
case "--approve-step-up":
|
|
6679
|
+
parsed.approveStepUp = true;
|
|
6680
|
+
break;
|
|
6681
|
+
case "--approved-by":
|
|
6682
|
+
parsed.approvedBy = requireValue(args, index, "--approved-by");
|
|
6683
|
+
index += 1;
|
|
6684
|
+
break;
|
|
6331
6685
|
case "--json":
|
|
6332
6686
|
parsed.json = true;
|
|
6333
6687
|
break;
|
|
@@ -6536,6 +6890,14 @@ function prepareTamperedSlackStep(actions, approvedBy) {
|
|
|
6536
6890
|
approvedPayloadHash: artifact.payloadHash,
|
|
6537
6891
|
attemptedPayloadHash: hashPayload(tamperedPayload),
|
|
6538
6892
|
mutation: "Slack message payload mutated after exact approval while reusing the original approval artifact.",
|
|
6893
|
+
scopedAuthority: {
|
|
6894
|
+
tool: target.action.capability.tool,
|
|
6895
|
+
resource: target.action.capability.resource,
|
|
6896
|
+
approvedBy: artifact.approvedBy,
|
|
6897
|
+
artifactId: artifact.id,
|
|
6898
|
+
scopeUnchanged: target.action.capability.tool === approvedAction.capability.tool &&
|
|
6899
|
+
target.action.capability.resource === approvedAction.capability.resource,
|
|
6900
|
+
},
|
|
6539
6901
|
};
|
|
6540
6902
|
}
|
|
6541
6903
|
function assertRealWorkflowModes(loadedConfig) {
|
|
@@ -6796,6 +7158,36 @@ function formatPolicyParityResult(result) {
|
|
|
6796
7158
|
["fixtures", result.fixturesPath],
|
|
6797
7159
|
["cedar", result.cedarPath],
|
|
6798
7160
|
["rego", result.regoWasmPath],
|
|
7161
|
+
[
|
|
7162
|
+
"agentcore",
|
|
7163
|
+
`${result.agentCore.status} · ${result.agentCore.fixtureCount} Cedar-shaped Gateway requests`,
|
|
7164
|
+
],
|
|
7165
|
+
],
|
|
7166
|
+
});
|
|
7167
|
+
}
|
|
7168
|
+
function formatAcsConformanceResult(result) {
|
|
7169
|
+
return renderWorkflow({
|
|
7170
|
+
command: "acs conformance",
|
|
7171
|
+
state: result.passed ? "ok" : "failed",
|
|
7172
|
+
verdict: result.passed
|
|
7173
|
+
? `All ${result.total} ACS intervention vectors passed`
|
|
7174
|
+
: `${result.failed} of ${result.total} ACS intervention vectors failed`,
|
|
7175
|
+
steps: result.vectors.map((entry) => ({
|
|
7176
|
+
state: entry.passed ? "ok" : "failed",
|
|
7177
|
+
name: entry.id,
|
|
7178
|
+
detail: `${entry.interventionPoint} -> ${entry.actualVerdict}` +
|
|
7179
|
+
` · proxy ${entry.actualProxyStatus ?? "none"}` +
|
|
7180
|
+
` · ${entry.reasons.join(", ")}`,
|
|
7181
|
+
})),
|
|
7182
|
+
rows: [
|
|
7183
|
+
["manifest", result.manifestVersion],
|
|
7184
|
+
["ledger", result.ledgerPath],
|
|
7185
|
+
[
|
|
7186
|
+
"actionpass",
|
|
7187
|
+
result.vectors.some((entry) => entry.actionPassId)
|
|
7188
|
+
? "issued for allow vector"
|
|
7189
|
+
: "not issued",
|
|
7190
|
+
],
|
|
6799
7191
|
],
|
|
6800
7192
|
});
|
|
6801
7193
|
}
|
|
@@ -6850,17 +7242,41 @@ function formatLedgerAttestResult(result) {
|
|
|
6850
7242
|
],
|
|
6851
7243
|
});
|
|
6852
7244
|
}
|
|
7245
|
+
/**
|
|
7246
|
+
* One line summarizing the approval↔execution equivalence over a set of
|
|
7247
|
+
* execution records. Verification fails closed on any failed pair, so a
|
|
7248
|
+
* summary rendered for a VALID export can only contain proved + unproven.
|
|
7249
|
+
*/
|
|
7250
|
+
export function equivalenceSummaryLine(report) {
|
|
7251
|
+
if (report.executions === 0) {
|
|
7252
|
+
return "no execution records";
|
|
7253
|
+
}
|
|
7254
|
+
const parts = [
|
|
7255
|
+
`${report.proved} of ${report.executions} execution${report.executions === 1 ? "" : "s"} proved approved == executed`,
|
|
7256
|
+
];
|
|
7257
|
+
if (report.unproven > 0) {
|
|
7258
|
+
parts.push(`${report.unproven} without approval evidence (unproven)`);
|
|
7259
|
+
}
|
|
7260
|
+
if (report.failed > 0) {
|
|
7261
|
+
parts.push(`${report.failed} FAILED`);
|
|
7262
|
+
}
|
|
7263
|
+
return parts.join(" · ");
|
|
7264
|
+
}
|
|
6853
7265
|
function formatVerifyExportResult(result) {
|
|
6854
7266
|
if (result.result.valid) {
|
|
7267
|
+
const rows = [
|
|
7268
|
+
["bundle", result.bundlePath],
|
|
7269
|
+
["issuer", result.result.claims.iss],
|
|
7270
|
+
["export digest", shortHash(result.result.recomputedDigest)],
|
|
7271
|
+
];
|
|
7272
|
+
if (result.equivalence) {
|
|
7273
|
+
rows.push(["equivalence", equivalenceSummaryLine(result.equivalence)]);
|
|
7274
|
+
}
|
|
6855
7275
|
return renderWorkflow({
|
|
6856
7276
|
command: "verify-export",
|
|
6857
7277
|
state: "ok",
|
|
6858
7278
|
verdict: `Export VALID · ${result.result.claims.recordCount} record${result.result.claims.recordCount === 1 ? "" : "s"} independently verified`,
|
|
6859
|
-
rows
|
|
6860
|
-
["bundle", result.bundlePath],
|
|
6861
|
-
["issuer", result.result.claims.iss],
|
|
6862
|
-
["export digest", shortHash(result.result.recomputedDigest)],
|
|
6863
|
-
],
|
|
7279
|
+
rows,
|
|
6864
7280
|
});
|
|
6865
7281
|
}
|
|
6866
7282
|
return renderWorkflow({
|
|
@@ -6873,6 +7289,35 @@ function formatVerifyExportResult(result) {
|
|
|
6873
7289
|
],
|
|
6874
7290
|
});
|
|
6875
7291
|
}
|
|
7292
|
+
function formatProveEquivalenceResult(result) {
|
|
7293
|
+
const report = result.report;
|
|
7294
|
+
const failed = report.entries.filter((entry) => entry.status === "failed");
|
|
7295
|
+
const stepRows = failed.map((entry) => ({
|
|
7296
|
+
state: "failed",
|
|
7297
|
+
name: `record ${entry.line}${entry.tool ? ` (${entry.tool})` : ""}`,
|
|
7298
|
+
detail: `${entry.failure} · approved ${entry.approvedPayloadHash ? shortHash(entry.approvedPayloadHash) : "absent"} vs executed ${entry.executedPayloadHash ? shortHash(entry.executedPayloadHash) : "absent"}`,
|
|
7299
|
+
}));
|
|
7300
|
+
return renderWorkflow({
|
|
7301
|
+
command: "prove-equivalence",
|
|
7302
|
+
state: report.failed > 0 ? "failed" : "ok",
|
|
7303
|
+
verdict: report.failed > 0
|
|
7304
|
+
? `Equivalence FAILED · ${report.failed} execution record${report.failed === 1 ? "" : "s"} broke approved == executed`
|
|
7305
|
+
: report.executions === 0
|
|
7306
|
+
? "No execution records to prove · the ledger chain is intact"
|
|
7307
|
+
: report.proved === 0
|
|
7308
|
+
? `No approved executions to prove · ${report.unproven} execution${report.unproven === 1 ? "" : "s"} without approval evidence · chain intact`
|
|
7309
|
+
: `Equivalence holds · ${equivalenceSummaryLine(report)}`,
|
|
7310
|
+
steps: stepRows,
|
|
7311
|
+
rows: [
|
|
7312
|
+
["ledger", result.ledgerPath],
|
|
7313
|
+
["executions", report.executions],
|
|
7314
|
+
["proved", report.proved],
|
|
7315
|
+
["unproven", report.unproven],
|
|
7316
|
+
["failed", report.failed],
|
|
7317
|
+
...(result.outputPath ? [["out", result.outputPath]] : []),
|
|
7318
|
+
],
|
|
7319
|
+
});
|
|
7320
|
+
}
|
|
6876
7321
|
/** Map a per-connector readiness status to a render state. */
|
|
6877
7322
|
function doctorProviderState(status) {
|
|
6878
7323
|
if (status === "ready")
|
|
@@ -6919,6 +7364,20 @@ function formatConnectorDoctorResult(result) {
|
|
|
6919
7364
|
* step failed before Slack, so the tamper was never exercised — must never be
|
|
6920
7365
|
* reported as the tampered payload executing).
|
|
6921
7366
|
*/
|
|
7367
|
+
/**
|
|
7368
|
+
* The scoped-authority contrast line — the differentiation beat spelled out:
|
|
7369
|
+
* the agent still held a valid, exact-payload human approval whose tool +
|
|
7370
|
+
* resource scope matched the attempted call, so an authorizer that checks
|
|
7371
|
+
* scope alone would have let the swapped payload through. Only claim that
|
|
7372
|
+
* when `scopeUnchanged` was actually computed true; otherwise state the
|
|
7373
|
+
* scope changed too, which any authorizer should reject.
|
|
7374
|
+
*/
|
|
7375
|
+
export function scopedAuthorityLine(authority) {
|
|
7376
|
+
const held = `valid approval by ${authority.approvedBy} for ${authority.tool} on ${authority.resource}`;
|
|
7377
|
+
return authority.scopeUnchanged
|
|
7378
|
+
? `${held} · scope unchanged - a scope-only check would allow this call; Axtary binds the approval to the exact payload hash`
|
|
7379
|
+
: `${held} · scope changed by the mutation`;
|
|
7380
|
+
}
|
|
6922
7381
|
export function tamperOutcomeLine(tamper) {
|
|
6923
7382
|
if (!tamper.reached) {
|
|
6924
7383
|
return "tamper step not reached - a prior step failed before Slack, so content binding was not exercised this run (re-run with a fresh --head). The tampered payload did NOT execute.";
|
|
@@ -6953,6 +7412,7 @@ function formatRealWorkflowRunResult(result) {
|
|
|
6953
7412
|
? "executed"
|
|
6954
7413
|
: "failed";
|
|
6955
7414
|
rows.push(["tamper", `${result.tamper.step} (${result.tamper.tool}) · ${result.tamper.mutation}`]);
|
|
7415
|
+
rows.push(["scoped authority", scopedAuthorityLine(result.tamper.scopedAuthority)]);
|
|
6956
7416
|
rows.push(["approved hash", shortHash(result.tamper.approvedPayloadHash)]);
|
|
6957
7417
|
rows.push(["attempted hash", shortHash(result.tamper.attemptedPayloadHash)]);
|
|
6958
7418
|
rows.push(["tamper outcome", `${stateGlyph(tamperState)} ${tamperOutcomeLine(result.tamper)}`]);
|
|
@@ -7349,7 +7809,7 @@ function helpText() {
|
|
|
7349
7809
|
return [
|
|
7350
7810
|
"Usage:",
|
|
7351
7811
|
" axtary init [--template repo-only-coding|staging-reads|incident-investigation|ticket-updates|doc-search|guarded-prod] [--config axtary.yml] [--force] [--json] (scaffold a starter config and optional scope-template harness)",
|
|
7352
|
-
" axtary demo [--config axtary.yml] [--template <name>] [--ledger .axtary/actions.jsonl] [--json]",
|
|
7812
|
+
" axtary demo [--config axtary.yml] [--template <name>] [--ledger .axtary/actions.jsonl] [--approve-step-up [--approved-by user:you@example.com]] [--json] (--approve-step-up attaches a local exact-payload approval so step-up actions execute with the approved/executed hash pair recorded)",
|
|
7353
7813
|
" axtary revoke <actionpass-jti> [--trust-store .axtary/actionpass-trust-store.json] [--status-store .axtary/actionpass-status.json] [--by user:you@example.com] [--reason 'task cancelled'] [--json]",
|
|
7354
7814
|
" axtary revocations [--trust-store .axtary/actionpass-trust-store.json] [--json]",
|
|
7355
7815
|
" axtary issuer-keys [--keyring .axtary/keys/actionpass-issuer-keyring.json] [--rotate] [--retire-after 1200] (metadata + public JWKS only; never prints private keys)",
|
|
@@ -7370,7 +7830,8 @@ function helpText() {
|
|
|
7370
7830
|
" axtary sync-ledger --endpoint https://app.example/api/ledger/sync [--config axtary.yml] [--ledger .axtary/actions.jsonl] [--from ISO] [--to ISO] [--decision allow|deny|step_up] [--tenant org:company] [--token-env AXTARY_LEDGER_SYNC_TOKEN] [--json]",
|
|
7371
7831
|
" axtary export-otel --endpoint http://127.0.0.1:4318/v1/traces [--config axtary.yml] [--ledger .axtary/actions.jsonl] [--from ISO] [--to ISO] [--decision allow|deny|step_up] [--service-name axtary-local] [--json] # opt-in OTLP/HTTP GenAI execute_tool spans, payload-free",
|
|
7372
7832
|
" axtary attest-ledger [--config axtary.yml] [--ledger .axtary/actions.jsonl] [--from ISO] [--to ISO] [--decision allow|deny|step_up] [--out attestation.json] [--key .axtary/keys/attestation-key.json] [--json]",
|
|
7373
|
-
" axtary verify-export <attestation.json> [--issuer https://local.axtary.dev] [--json] # standalone: signature + chain + digest",
|
|
7833
|
+
" axtary verify-export <attestation.json> [--issuer https://local.axtary.dev] [--json] # standalone: signature + chain + digest + approval↔execution equivalence",
|
|
7834
|
+
" axtary prove-equivalence [--config axtary.yml] [--ledger .axtary/actions.jsonl] [--from ISO] [--to ISO] [--decision allow|deny|step_up] [--out equivalence.json] [--json] # per-execution approved_payload_hash == executed_payload_hash proof, exportable",
|
|
7374
7835
|
" axtary prove-inclusion <attestation.json> (--record <jti> | --index <n>) [--out proof.json] [--json] # RFC 6962 inclusion proof",
|
|
7375
7836
|
" axtary prove-consistency <first.json> <second.json> [--out proof.json] [--json] # RFC 6962 consistency proof across two heads",
|
|
7376
7837
|
" axtary verify-proof <proof.json> (--bundle <b.json> | --first <a.json> --second <b.json>) [--issuer ...] [--json] # standalone proof verifier",
|
|
@@ -7396,6 +7857,7 @@ function helpText() {
|
|
|
7396
7857
|
" axtary policy test <policies.test.yml> [--config axtary.yml] [--json] (CI policy harness)",
|
|
7397
7858
|
" axtary test-policy --fixtures ./fixtures [--config axtary.yml] [--json]",
|
|
7398
7859
|
" axtary test-policy --parity [--fixtures parity-fixtures.json] [--cedar <file.cedar>] [--rego-wasm <policy.wasm>] [--json]",
|
|
7860
|
+
" axtary acs conformance [--config axtary.yml] [--ledger .axtary/acs-conformance.jsonl] [--json] (credential-free ACS pre_tool_call vectors through Axtary policy, ActionPass, and ledger)",
|
|
7399
7861
|
"",
|
|
7400
7862
|
].join("\n");
|
|
7401
7863
|
}
|