Skip to content

Samples — HARP

Reference implementations

Working samples of the HARP-CORE spec in six languages — all wire-compatible.

All six implementations produce wire-compatible artifacts and decisions — encrypted by any one, decryptable by any other — provided the same key material is used.

C# (.NET 8)

Crypto: NSec (libsodium) · Runner: dotnet run
View source →

Node.js (ESM)

Crypto: libsodium-wrappers-sumo + node:crypto · Runner: node
View source →

Python (3.9+)

Crypto: PyNaCl + hashlib · Runner: python
View source →

TypeScript (strict)

Crypto: libsodium-wrappers-sumo + node:crypto · Runner: tsx
View source →

Go (1.26+)

Crypto: x/crypto + crypto/ed25519 (stdlib) · Runner: go run
View source →

Rust (2021 ed.)

Crypto: RustCrypto (pure Rust, no C deps) · Runner: cargo run
View source →

Every implementation follows the same flow:

1

Generate MA keys (first run only)

Run the approver binary once to generate the Mobile Approver's Ed25519 signing and X25519 key-exchange keypairs.

2

Build & encrypt artifact

Run the executor to create a sample artifact, canonicalize it (RFC 8785 JCS), compute SHA-256 hash, and encrypt via X25519 + XChaCha20-Poly1305.

3

Decrypt, review & sign

Run the approver again — it decrypts the artifact, displays it for review, and signs a Decision with Ed25519 upon approval.

4

Verify signature & enforce

Run the enforcer to verify the Ed25519 signature, check artifact hash binding, validate expiry, and check the nonce journal for replays.

LanguageApprover (Steps 1 & 3)Executor (Step 2)Enforcer (Step 4)
C#dotnet run --project Harp.Approverdotnet run --project Harp.Executordotnet run --project Harp.Enforcer
Node.jsnode harp-approver.mjsnode harp-executor.mjsnode harp-enforcer.mjs
Pythonpython harp_approver.pypython harp_executor.pypython harp_enforcer.py
TypeScriptnpx tsx src/harp-approver.tsnpx tsx src/harp-executor.tsnpx tsx src/harp-enforcer.ts
Gogo run ./cmd/harp-approver/go run ./cmd/harp-executor/go run ./cmd/harp-enforcer/
Rustcargo run --bin harp-approvercargo run --bin harp-executorcargo run --bin harp-enforcer

All implementations share the same cryptographic stack:

FunctionAlgorithmPurpose
Key ExchangeX25519Derive shared secret between HE and MA
Key DerivationHKDF-SHA256Derive symmetric AEAD key from shared secret
Payload EncryptionXChaCha20-Poly1305AEAD encryption of artifact payload
Artifact HashingSHA-256Deterministic hash of canonicalized artifact
Decision SigningEd25519Human-bound cryptographic approval signature
CanonicalizationRFC 8785 (JCS)Deterministic JSON for cross-platform hash agreement
{
"requestId": "...",
"artifactType": "command.review",
"repoRef": "repo:opaque:demo",
"createdAt": "2026-02-24T...",
"expiresAt": "2026-02-24T...",
"artifactHashAlg": "SHA-256",
"artifactHash": "<64-hex>",
"enc": {
"kdf": "X25519+HKDF-SHA256",
"encAlg": "XChaCha20-Poly1305",
"maKxPub": "<base64url>",
"heKxPub": "<base64url>",
"salt": "<base64url>",
"info": "HARP-XCHACHA-PAYLOAD-V1",
"nonce": "<base64url>",
"ciphertext": "<base64url>",
"tag": "<base64url>"
}
}
{
"requestId": "...",
"artifactHashAlg": "SHA-256",
"artifactHash": "<64-hex>",
"repoRef": "repo:opaque:demo",
"decision": "approve",
"scope": "once",
"expiresAt": "2026-02-24T...",
"nonce": "<base64url>",
"sigAlg": "Ed25519",
"signerKeyId": "ma-key-1",
"signature": "<base64url>"
}
  • Confidential payload — XChaCha20-Poly1305 AEAD with X25519 key exchange
  • Integrity — AEAD authentication + AAD binding
  • Cryptographic approval binding — Ed25519 signature over artifactHash
  • Signature authenticity — Ed25519 verification with known signerKeyId
  • Replay resistance — Nonce journal + expiration enforcement
  • Deterministic canonicalization — RFC 8785 JCS for cross-platform hash agreement

Each implementation supports 7 rejection scenarios:

  1. Replay detection — Run enforcer twice; nonce journal blocks reuse
  2. Deny decision — Type n at approval prompt; enforcer exits with code 2
  3. Tampered artifact — Edit repoRef in artifact-wire.json; hash mismatch
  4. Tampered decision — Edit decision field without re-signing; signature fails
  5. Expired artifact/decision — Wait beyond TTL; expiry check rejects
  6. Wrong key — Regenerate keys; old decision signature fails
  7. Binding mismatch — Edit requestId in decision.json; binding check fails