This page reorganizes evidence CLAIMS.md already cites, plus a small number of tests added
specifically to close gaps this page’s own research found, into objection-first language.
It never introduces a claim CLAIMS.md doesn’t already make — see The CLAIMS.md
discipline for why that ordering matters. Compliance
claims (SOC2, GDPR, ISO 27001, a specific regulator’s approval) are deliberately absent:
CLAIMS.md §5 permanently refuses “guaranteed regulatory compliance” as a claim no
implementation could honestly back — see What Parmana does not
claim. This page indexes evidence for claims already
made, it does not manufacture evidence for claims that were never made.
How to read this page
Each row names an objection a skeptical reviewer would actually raise, then answers it one of three ways:- Existing test — cites the real file and test name. Run it yourself.
- New test (added while building this page) — same, plus a one-line note on what gap it closed.
- Honest limit — the objection’s premise doesn’t hold for this architecture, or the guarantee is real but narrower than the objection assumes. Stated precisely, not glossed over.
npm test yourself — 1482 passed, 37 pre-existing skips, 0
failed, as of this page’s last update.
Domain 1: Authorization and verification
| Objection | Answer |
|---|---|
| A caller lacking a capability might still get to execute it | Existing test. packages/api/tests/integration/caller-capability-scoping.integration.test.ts: blocks a caller scoped to a different capability before policy evaluation ever runs; a caller with no allowedCapabilities configured is denied every capability, fail-closed by default. |
| A tampered or forged signed authorization might slip through | Existing test. packages/crypto/tests/unit/authorization-envelope.test.ts: tampered decisionId, expiresAt, signalsHash, grantedCapability, submittedBy (added while building this page — see below) all independently proven to fail signature verification, plus a signature from the wrong key. |
| The policy layer could approve something the caller was never cleared for, and the connector would just trust it | Existing test, closed this session. packages/execution-control/tests/unit/connector-policy-granted-capability.test.ts: execution is rejected when the signed authorization’s grantedCapability names a different action than the one actually executed. See Execution authorization and docs/CLAIMS.md §2.31 for the honest scope of what this closes (a future code path bypassing today’s single verified route) and what it doesn’t (an attacker with in-process code execution, who could forge this exactly as easily as anything else). |
| Would a jailbroken or adversarial agent’s request just work if it asked for something out of scope? | Existing test, same evidence as row 1. The capability check (allowedCapabilities.includes(action)) takes no input describing the requester’s internal state or intent — a “jailbroken” request and a merely mistaken one are denied by the identical, unconditional check. |
| A policy edited after an authorization was signed might silently change what that authorization approved | Existing test. packages/execution-gateway/tests/unit/policy-freshness.test.ts: an authorization is rejected at execution time, with the exact content mismatch named, when the policy’s content changed since signing (docs/CLAIMS.md §2.27). The original authorization’s own terms are never retroactively altered — a policy change affects only authorizations signed after it. |
| Is “scope” actually enforced from a signed fact, or does the connector just re-trust whatever the policy layer says today? | Existing test, same evidence as row 3. grantedCapability is signed into ExecutionAuthorizationPayload at authorization time and checked at the connector layer against the payload, not re-derived by asking the policy layer a second time. |
| Is the connector-layer check actually independent, or a second copy of the same code path as the API-layer check? | Existing test. packages/api/tests/unit/isCapabilityAllowed.test.ts (API edge: caller vs. their API key’s grant) and connector-policy-granted-capability.test.ts (connector edge: signed claim vs. executed action) are two different mechanisms over two different data sources, each independently tested. |
| Could a caller identity be spoofed by editing the payload after signing? | New test, added while building this page. authorization-envelope.test.ts, “rejects a tampered submittedBy” — every other signed field already had a dedicated tamper test; this one didn’t, though the general “every field participates in the signature” guarantee already covered it. Gap was in test citability, not in the mechanism itself. |
| Is an authorization’s expiry actually checked, or just recorded? | Existing test. authorization-envelope.test.ts, “rejects an expired authorization with a valid signature.” |
| Does every connector get the same caller-capability check, or could one connector implement a weaker version? | Existing test. packages/api/src/bootstrap/createConnectorRegistry.ts wires DefaultConnectorPolicy identically for every registered connector (HubSpot, GitHub, and the test fixture) — one class, no per-connector override to weaken. |
| Is signature re-verification real cryptography, or just a boolean flag being checked? | Existing test. @parmana/envelope-verifier and AuthorizationVerifier perform real Ed25519 (or ML-DSA-65, see Choose a signature provider) signature verification — “rejects a signature from a different key” only passes because the actual cryptographic check runs. |
| What if the signing key itself is stolen? Does re-verification catch a forged authorization signed with the real key? | Honest limit — no test claims this, and none could. If an attacker holds the actual private key, anything they sign (including a fabricated grantedCapability) verifies as a fully legitimate signature, because it is one — this is what “key compromise” means for every signature scheme, not a gap specific to this codebase. Signature re-verification can only catch a payload that was not signed by the legitimate key, or altered after legitimate signing; it cannot distinguish the real key holder from a thief holding the same key. This repo’s actual posture: docs/CLAIMS.md §2.28 (key-id-aware resolution with expiry/revocation) limits exposure after a compromise is discovered and the key is revoked — a mitigation, not a prevention. There is currently no automated key rotation and no HSM/KMS-backed KeyProvider (aws-kms/azure-key-vault/gcp-kms/hsm are declared config values with zero implementing classes — see What Parmana does not claim): keys are plain files on disk, and protecting them is entirely an operational, not a code-level, control today. |
Domain 2: Credential management
The premise behind several of these objections — that a credential itself carries an
embedded, per-action “scope” claim like
transfer_up_to_500 — doesn’t match this
architecture. SessionCredentialVault.issue(connectorId, authorizationId)
(packages/execution-control/src/SessionCredentialVault.ts) scopes a credential to a
connector, single-use, time-bounded. The action and parameters actually executed are
pinned earlier and separately, by the signed businessTransactionHash on the authorization
itself, before a credential is ever issued — a session credential is never asked “does this
permit action X,” only “is this connector’s secret available, unused, unexpired,
unrevoked.” The rows below answer the real mechanism, not the assumed one.| Objection | Answer |
|---|---|
| How do you ensure a credential is actually scoped down, not general-purpose? | Existing test. packages/execution-control/tests/unit/session-credential-vault.test.ts: issue() never resolves or holds the underlying secret itself, confirmed by reading SessionCredentialVault.ts. |
| Could an agent use its credential for an action other than what was authorized? | Honest limit, real mechanism differs from the premise. There’s no separate “does this credential permit action X” check to test, by design — SessionCredentialSecureConnector.execute() always passes the connector’s executor the exact frozen executableContent from the one verified request, never an action derived from or checked against the credential. Enforcement is upstream (content-binding hash + ConnectorPolicy), not at the credential. |
| Is there a backdoor path that creates a credential without going through the scoped issuance flow? | Existing test. A repository-wide search for every .issue( call on a credential vault finds exactly one production call site: SessionCredentialSecureConnector.ts. |
| If a credential leaks, is it still usable after expiry or revocation? | Existing test. session-credential-vault.test.ts: rejects a credential after expiry and after revocation, including the exact boundary instant (expiresAt itself is expired, one millisecond before is not). |
| Can a credential’s scope be independently, cryptographically proven — not just trusted as in-memory state? | Honest limit, real gap. A SessionCredential’s connector/authorization scoping is plain in-memory session state, not itself signed. The provable, signed guarantee lives one layer up, on ExecutionAuthorizationPayload (content hash, and now grantedCapability, §2.31) — not on the session credential in isolation. No test claims otherwise. |
| Can a credential be used twice? | Existing test. “rejects reuse of an already-consumed session credential.” |
| Is the revocation check optional, or could some code path skip it? | Existing test. consume()/revoke() sit inside an unconditional try/finally in SessionCredentialSecureConnector.ts — every execute() call path runs them. |
| Is there a timing window where a credential could be consumed twice under concurrency? | Existing test. “under two concurrent consume() calls on one session, exactly one succeeds” — deterministic by construction (the used flag is set synchronously before the only await), not a probabilistic pass. |
| If execution fails downstream, is the credential still burned, or can it be retried indefinitely? | Existing test. “destroys the session credential after a failing execution” — revoke() runs in finally, so a failed executor call still consumes the credential. |
| Does a retry after failure get a fresh credential, or reuse the dead one? | Existing test, true by construction. A consumed session always throws on reuse (“rejects direct invocation with an already-consumed session”), so a retry necessarily requires a fresh authorization and credential — there is no code path that could hand back the same one. |
Domain 3: Audit trail and evidence
This repo has three separate signed/durable record mechanisms, not one —
ExecutionTrustRecord
(every execution, success or policy-rejected), RefusalRecord (RFC-0021, a policy REJECT’s
own dedicated evidence), and CallerAuditEvent (the caller-authentication layer, CallerAuditSink).
Rows below name which mechanism actually applies.| Objection | Answer |
|---|---|
| Could an execution outcome go completely unrecorded? | Existing test. packages/api/tests/integration/structural-validation-audit.integration.test.ts covers malformed/oversized bodies, malformed IDs, structural validation failure, and duplicate submission — each produces a CallerAuditEvent. Policy denials produce a RefusalRecord; successful executions produce an ExecutionTrustRecord. Every outcome category has a durable record, via one of the three mechanisms. |
| Could an audit or trust record be deleted after the fact? | Existing test (by absence). No delete/remove method exists anywhere on ExecutionTrustRecordRepository, RefusalRecordRepository, or any CallerAuditSink implementation. |
| Is an audit entry missing context that would matter to an investigator (who, what, when)? | Existing test. Each CallerAuditEvent type’s required field subset is exercised across the structural-validation-audit suite; no single test asserts every field for every type in one place, but no field is untested for the event type it applies to. |
| Could an audit entry be lost if the process crashes right after logging it? | Existing test. createCallerAuditSink.ts fails closed at startup (assertDatabaseUrlConfigured) unless NODE_ENV=test — production never silently falls back to an in-memory sink; every write goes to durable Postgres. |
| Could an audit or trust record be edited without detection? | Existing test, two mechanisms. AuditEventCrypto/SupabaseCallerAuditSink sign every CallerAuditEvent (proven in packages/api/tests/integration/audit-verify.integration.test.ts, “reports a tampered audit event as invalid, not as a crash,” and now also in a dedicated unit test, see below). ExecutionTrustRecord.trustRecordHash tamper detection is proven in verification-service.test.ts. |
| Can an audit record be verified without trusting Parmana’s running process? | Existing test for ExecutionTrustRecord (Verify a trust record independently, demonstrated with the server stopped). New test for CallerAuditEvent, added while building this page — see below; this specific artifact type had no standalone, no-server proof before now. |
| Does the verification key change without notice, breaking old verifications? | Existing test. docs/CLAIMS.md §2.28, key-id-aware resolution with expiry/revocation checking. |
| Are verification tools proprietary, or something only Parmana can run? | Existing, trivially. @parmana/envelope-verifier and @parmana/crypto are ordinary packages in the public repository. |
| Does verification require a live network call to Parmana or anyone else? | Existing test, same evidence as the offline-verification row above. EnvelopeVerifier/AuditEventCrypto make no network calls; verification needs only the artifact, its signature, and a public key file. |
| If someone deleted a row from the audit table, would anything notice? | Closed. SupabaseCallerAuditSink.record() now chains each caller’s events to their own immediately-preceding event (previousChainHash/chainHash, folded into the same object AuditEventCrypto already signs — no second signature column), using a Postgres advisory lock scoped per caller rather than a table-wide lock, since caller_audit_events is the highest-write-volume table in this system. CallerAuditChainVerifier (packages/crypto/src/CallerAuditChainVerifier.ts) proves a deleted row breaks the chain, standalone, with no server or database. See docs/CLAIMS.md §2.32 for the full design, including its honest limits (an entire caller’s history deleted at once, or cross-caller reordering, isn’t caught). |
Domain 4: Fail-closed behavior
| Objection | Answer |
|---|---|
| If the database is unreachable at startup, does the server start anyway? | Existing test. packages/api/tests/unit/bootstrap/create-nonce-store.test.ts, create-caller-audit-sink.test.ts: “(G-13) fails closed with a named, actionable error when NODE_ENV is not test and DATABASE_URL is not configured.” |
| If signature verification fails, does anything retry it, possibly against a different, more permissive check? | Existing test (by absence). No retry logic exists anywhere in EnvelopeVerifier/AuthorizationVerifier’s verify path — denial is immediate and single-pass. |
| If verification takes too long, does it default to allowing the request through? | Honest limit. No timeout mechanism exists in ExecutionGateway.verify() or RuntimeEngine.execute() at all — the pipeline is fully synchronous. There is no “times out and allows” failure mode to test, because there is no timeout concept in this code path in the first place; whatever timeout behavior exists comes from underlying infrastructure (HTTP server, database driver), not from Parmana’s own logic. |
| If some checks pass and only one fails, does the request still get approved? | Existing test. ExecutionGateway.ts’s priorChecksPassed ANDs every independent check; policy-freshness.test.ts and signal-freshness.test.ts each prove a single named check failing (with every other check passing) still denies the whole request. |
| If a connector is unreachable, does the request queue, retry, or silently succeed? | Existing test. Every connector adapter (github, hubspot, the generic http adapter) has a dedicated “fails closed on a non-2xx response” and “fails closed on a timeout, never returning a partial success” test. |
| When something fails, does the audit trail say why, or just that it failed? | Existing test. Reasons are specific, asserted-by-name strings (e.g. a policy rule’s own rejection reason, a named hash mismatch), not a generic boolean, across RefusalRecord and CallerAuditEvent tests. |
| If the system recovers from a transient failure, is that recovery itself observable? | Honest limit, arguably not applicable. This system has no background-retriable job model — every attempt is its own independently-audited request/response cycle. “Recovery” as a distinct event doesn’t exist as a concept to test; each retry is simply a new, separately-audited attempt. |
Domain 5: Performance
Before this page, zero committed, automated performance measurements existed anywhere in
this repository — only a manual script (
latency-test.cjs) requiring a live deployed key
and manual invocation, and an investigation
(docs/investigations/2026-08-10-latency-and-voice-ai-readiness.md) that explicitly states
a full POST /execute round trip “remains unmeasured.” The two tests below close part of
that gap — the part safe to measure deterministically in CI — and are explicit about what
they still don’t cover.| Objection | Answer |
|---|---|
| Does cryptographic signing/verification add meaningful latency? | New test. packages/crypto/tests/unit/authorization-signing-performance.test.ts, 200 in-process sign/verify round trips: 0.102ms/op to sign, 0.142ms/op to verify (Ed25519, measured on this session’s machine — re-run it yourself, numbers will vary by hardware). No network, no database. |
| Does the full authorization/execution pipeline add meaningful latency? | New test, narrower than the objection. packages/runtime/tests/unit/execution-pipeline-latency.test.ts, 50 in-process RuntimeEngine.execute() calls (validate → evaluate policy → sign → execute → assemble trust record, in-memory DefaultExecutionSystem, no real connector): avg 3.71ms, p50 3.40ms, p99 8.10ms. This explicitly excludes HTTP overhead, Express middleware, real Postgres/Supabase writes, and a real connector’s network round trip — the still-open question the linked investigation raises. |
| Can the system handle high throughput? | Honest limit, not closed. No load/throughput test exists. The two tests above measure per-operation cost, not sustained concurrent throughput; extrapolating one from the other would be a claim this page isn’t making. |
| Does the system misbehave (e.g. default-allow) under load-induced timeouts? | Honest limit, same as the fail-closed timeout row above. No timeout concept exists in this code path to misbehave under load in the first place; a real answer here needs the throughput test that doesn’t exist yet, run against whatever HTTP/database timeout behavior a real deployment actually has. |
What this page deliberately did not build
- No test for a claim CLAIMS.md doesn’t make. SOC2, GDPR, ISO 27001, and similar were considered and dropped — see the note at the top of this page.
- No throughput/load test. Real load testing needs a decision about target hardware and acceptable concurrency, not something this page should assume on your behalf.
Next
The CLAIMS.md discipline
How every citation on this page traces back to a real file or test.
What Parmana does not claim
The permanent boundaries this page’s scope respects.