This page is a direct mirror of
docs/CLAIMS.md sections 3 and 5, read that
file for the authoritative version.Claims that hold, with their scope
Non-bypassable envelope verification (Conditional Claim 3.1). For any system running@parmana/envelope-verifier, execution requests not authorized by Parmana are
cryptographically impossible to accept, only for a receiving system that (a) runs the
verifier and (b) gates every execution-triggering code path behind its result, and only
while Parmana’s authorization signing key is not compromised: whoever holds that key, or can
make the signing service sign, can produce authorizations the verifier accepts. Parmana
enforces nothing at the network level. See the Audit guide for
what an attacker controls in each case. The default local server does both (a) and (b) for
each connector it registers, see The gateway for the mechanism. A new
built in connector is a code change; an external connector
is registered through maker checker with no code change, and Parmana signs each release to it.
Fleet wide single use requires a shared NonceStore (3.2). Single use enforcement is
scoped to whichever NonceStore instance performs the check. Parmana’s own server shares one
in Postgres (SupabaseNonceStore, and the same for approvals), so every instance on one
database accepts an authorization once, and it fails closed at startup without one. A
receiving system that runs @parmana/envelope-verifier with its own per instance store can
accept the same authorization once per instance; MemoryNonceStore, for tests, loses all
state on restart. Every envelope’s bounded TTL limits, but does not eliminate, that exposure.
Durable, third-party-verifiable refusal and audit records (3.11). Every
policy rejection produces a signed Refusal Record, independently verifiable at POST /refusal/verify with no API key or database lookup required, just the artifact and
Parmana’s public key. Production caller-authentication audit events
are signed the same way, verifiable at POST /audit/verify. Two scope caveats: Refusal
Record writing is evidentiary and fails open by design (a rejection is never blocked or
delayed by it; only the durable record can go silently missing, never the correctness of
the rejection itself), and only production (Supabase) audit sinks sign; the in-memory
test sinks don’t.
Hybrid (Ed25519 + ML-DSA-65) signing capability, not yet deployed (3.13). Trust
Records and Receipts can be dual-signed with both a classical and a post-quantum
algorithm at once, with verification requiring both signatures independently, fail-closed.
This is a built, tested capability (CRYPTO_MODE=hybrid is real, validated
configuration), but it is opt-in and not running anywhere today: every deployed
environment signs Ed25519 alone.
Third-party verification: @parmana/sign 0.2.0 and later, the public verification
SDK on npm, recognizes the hybrid envelope: verifyExecutionTrustRecordOffline checks
the record hash, the legacy Ed25519 signature, and every entry in signatures, offline,
with only the record and the public keys. This repository’s own
packages/crypto/src/OfflineVerifier.ts (TypeScript) does the same, and
python/parmana/crypto/offline_verifier.py (Python) checks Ed25519 only.
Hybrid-signature downgrade resistance, opt-in. A genuinely hybrid-signed record’s
signatures array (the ML-DSA-65 half) can be stripped entirely with no cryptographic
trace: schemaVersion and signatures are outside the hashed content by design (so the
legacy Ed25519 signature keeps verifying pre-hybrid-era records unchanged), and
verification silently falls back to the classical signature alone whenever the array is
absent. An opt-in HYBRID_SIGNATURE_REQUIRED flag closes this: when set, a record missing
its signatures array is rejected outright, no fallback. Off by default, so this protects
only a deployment that turns it on once every record it issues going forward is genuinely
hybrid-signed.
Claims Parmana intentionally does not make
Not “not yet” items, no implementation could honestly back these, unscoped:- Execution is impossible to bypass under all circumstances.
- Mathematical proof of execution correctness.
- Cryptographic proof of every aspect of runtime behavior.
- Guaranteed regulatory compliance.
- Absolute prevention of all unauthorized execution.
- Tamper-proof operation in every deployment environment.
- “Non-bypassable” or “the single execution authority” as an unscoped, system-wide claim, envelope verification is opt-in per receiving endpoint (see 3.1 above).
- Deterministic signature output for ML-DSA-65, those signatures are randomized by design; only verification is deterministic.
Known incidents
Policy-evaluation signals were not bound to the executed Intent (2026-07-28, RESOLVED). The most severe gap found in this project’s history: a live, reproducible bypass of the core “no unauthorized execution” invariant.PolicyEngine evaluated whatever signals a caller
declared in the request body; ExecutionGateway signed and executed intent.target/
intent.parameters, a completely disjoint set of fields nothing cross-checked. A caller
could declare signals describing a small, fully-verified action while intent executed
something else entirely, and still receive a signed, APPROVED Execution Trust Record for
it. Live proof-of-concept: signals declared a fully verified, policy-approved 999,999,999. Before the fix: 200, policy decision
APPROVED, execution COMPLETED, a real signed trust record and receipt issued for it, the
exact artifacts this project’s independently-verifiable-execution claim rests on, attesting
to something that never happened as described. A related, compounding finding:
authority.principalId (who the trust record says approved the action) was likewise
caller-declared with no binding to the identity callerId actually proves, any caller
holding any valid API key could claim to be any human or role, including a successful
“impersonate the CEO” proof-of-concept. Found via an external adversarial security exercise,
not this project’s own internal audit process.
Fixed by Policy.boundSignals + SignalIntentBinder (binds specific signal keys to intent
dot-paths, checked before PolicyEngine.evaluate; see Policies and the
decision for the mechanism), isPrincipalAllowed
(binds authority.principalId to the authenticated callerId), and a compounding IDOR fix,
isOwnedByCaller (any caller could previously read any other caller’s complete
transaction/trust-record/receipt history). 28 regression tests reproduce the exact live
exploit shapes and assert they’re now rejected, plus positive controls proving legitimate
requests are unaffected. One thing this fix deliberately does not cover, stated plainly
rather than left implicit: boundSignals only binds the specific fields a policy author
declares, it does not independently verify that an unbound signal (vendorVerified,
riskScore, and similar) is actually true; those remain caller-declared attestations.
The default signing key committed to this repository before 2026-07-05 was publicly
exposed in the public GitHub repository and must be treated as permanently compromised,
all signatures produced by that key are void for authenticity purposes regardless of when
signed. The key pair was rotated on 2026-07-05.
Four ML-DSA-65 (Dilithium3) private signing keys committed to a feature branch during the
post-quantum signature provider work were confirmed, by raw key-byte comparison, to be
distinct from any key ever trusted by production or local configuration. Unlike the incident
above, no signature produced by this key pair was ever accepted as authentic by this
codebase. A fresh keypair was generated regardless, the feature branch’s history purged, and
the exposure independently verified across every remote hosting this repository’s history.
Caller authentication, principal binding, and ownership scoping: three separate layers
packages/api gates every route behind a caller bearer key, except the liveness/readiness
and documentation/verification routes listed on
Authentication. Three distinct claims, not one, and it
matters which one you’re relying on:
- Route access is scoped by kind of caller, not per route. There is no per key allowlist
of endpoints, but every governance route (policy changes, approvers, external connectors,
the Execution Intent list and finalize) refuses a key not provisioned as a human, with
403 NON_HUMAN_CALLER_DENIED, andPOST /executerefuses a capability the key may not invoke (403 CAPABILITY_NOT_ALLOWED). - Principal assertion is scoped, the fix described in “Known incidents” above. A caller
may only submit a transaction whose
authority.principalIdis itself, or an identity explicitly granted viaApiKeyEntry.allowedPrincipalIds, never “anything,” the default with no grant configured is “only itself.”isPrincipalAllowed,packages/api/src/auth/isPrincipalAllowed.ts. - Data ownership is scoped, the same fix. A caller can no longer read another caller’s
transactions, trust records, or receipts,
metadata.submittedByis stamped server-side from the authenticated caller (never trusted from the client), andisOwnedByCallergates every read route by it; cross-caller access reads as a clean404, not a403that would confirm the target id exists.packages/api/src/auth/isOwnedByCaller.ts.
Automated checks on the repository
Every pull request tomain in
github.com/pavancharak/parmana must pass these before it can
merge. main is protected: no direct pushes, no force pushes, no deletion.
Also enabled on the repository:
- OpenSSF Scorecard: a public
supply chain security score, recomputed weekly and on every push to
main. - OpenSSF Best Practices: the project’s answers to the OpenSSF Best Practices criteria. The badge is “in progress”, not “passing”: the passing level requires the whole project to be open source, and only the SDKs are.
- Dependabot alerts and grouped weekly update pull requests for npm, Python, the Dockerfile and GitHub Actions.
- GitHub secret scanning with push protection, which blocks a commit that contains a known secret format.
- Private vulnerability reporting, alongside the email route in SECURITY.md.
- GitHub Actions and Docker base images pinned to exact commits and digests, and workflow tokens limited to read access.
- Signed build provenance and a CycloneDX SBOM for every SDK release, and signed build provenance for the server image published with each release (below).
- Mutation testing of the security-critical packages, run by hand (
npm run mutation). The scores, the gaps found and what was fixed are in docs/MUTATION-TESTING.md. - Fuzz tests (property based, with fast-check) of the API request boundary, policy loading and
evaluation, the offline verifier and approval parsing, run with every
npm test. They found three crashes on malformed input, all fixed (G-87, G-90, G-91 indocs/VERIFICATION-GAPS.md). - Every open issue, with what to do meanwhile: Limitations.
- Each risk in the OWASP Top 10 for LLM Applications and the OWASP Top 10 for Agentic Applications (2026), with what Parmana does and the evidence: OWASP mapping.
- Human oversight and record keeping under the EU AI Act (Articles 12, 14, 26), the NIST AI RMF, ISO/IEC 42001 and RBI guidance: Regulation mapping.
Verifying an SDK package from a GitHub release
Each GitHub release of the SDKs carries the four packages built by GitHub Actions from the release’s tag, and amultiple.intoto.jsonl file:
SLSA build provenance, signed with GitHub’s identity for the release
workflow, recording the commit, the workflow and each file’s SHA-256. To check a downloaded
package with slsa-verifier:
@parmana/sdk and parmana 1.5.0, the connector SDKs 0.2.0) were built before this workflow
existed, so they are not byte for byte the files in release sdk-v1.5.0-r1, although they come
from the same source.
Each npm package and each wheel also has a CycloneDX 1.5 SBOM beside it,
named after it (for example parmana-sdk-1.5.0.tgz.cdx.json): the package, and every dependency
installed with it, with versions and package URLs. For the npm packages it lists runtime
dependencies only; for parmana it includes the optional verify extra (cryptography). The
versions are the ones resolved when the release was built; a later install can resolve newer
versions within the declared ranges. The provenance covers the SBOM files too, so the same
slsa-verifier command checks them. Feed an SBOM to a scanner such as
Grype or OSV-Scanner to
check the dependencies for known vulnerabilities.
Verifying the server image
From the first release published after 2026-10-06, each release also builds the API server from the repository’sDockerfile and pushes it to the GitHub Container Registry as
ghcr.io/pavancharak/parmana-api:<release tag>, with SLSA build provenance attached to the image
in the registry. To check an image before you run it: