Architecture¶
Holon VC is one Rust library (holon_vc) and one executable (holon-vc). The CLI
coordinates explicit application services; cryptographic operations, authorization,
trust policy, evidence assessment, and final decisions remain separate.
System boundaries¶
flowchart TB
User[Operator or verifier application] --> CLI[CLI and application orchestration]
CLI --> Keys[Encrypted keys and suite bindings]
CLI --> Issuance[Credential and presentation services]
CLI --> Verify[Verification stages]
CLI --> Publish[Status and public artifact generation]
Issuance --> Crypto[Data Integrity suites]
Verify --> Crypto
Publish --> Crypto
Crypto --> RDF[Pinned JSON-LD and RDF canonicalization]
Verify --> Identity[DID authorization and bounded resource resolution]
Verify --> Policy[Scoped trust policies and evidence comparisons]
Verify --> Replay[Persistent replay store]
Verify --> Report[Structured report and decision]
Identity --> Remote[HTTPS resources or pinned offline copies]
Publish --> Files[Staged public files]
Files -. operator deploys .-> Remote
The Rust executable does not call Python or Node for production cryptography. Python runs the executable documentation and builds this site. Test-only JavaScript provides an independent implementation for interoperability checks.
The issuer's static HTTPS server is outside the process. Generation stages files; an operator deploys them. Optional OpenID metadata describes an externally operated service, not an HTTP endpoint implemented by the CLI.
Module responsibilities¶
Module names correspond to files under src/.
| Layer | Modules | Responsibility |
|---|---|---|
| Process boundary | main, cli, app, config |
Clap command parsing, configuration precedence, operation dispatch, private directory lock, output and exit codes |
| Input model | models, holon, schemas |
Duplicate-property rejection, typed Holon structures, semantic validation, pinned full/disclosure schema profiles |
| Identity and secrets | keys, key_storage, public_keys, suites |
Key generation, encrypted envelopes, fingerprints, explicit key/controller/purpose/suite bindings |
| Cryptographic document processing | jsonld, canonicalization, suites, disclosure |
Context policy, RDF expansion/canonicalization, proof signing/verification, actual statement derivation |
| Credential lifecycle | credentials, status, presentations |
Issue, derive, reissue, manage status, package credentials, bind holder proofs, consume challenges |
| Resolution | did, resolvers |
did:web translation, verification relationships, bounded HTTPS, digest pins and cache age |
| Decisions | verification, trust, evidence, reports |
Independent checks, scoped authority, explicit comparisons, warnings, confidence and decisions |
| Public discovery | well_known |
DID/linkage/JWKS/OpenID/Holon metadata generation, manifests and consistency validation |
| Persistence and failures | storage, errors |
Descriptor-relative Linux files, atomic writes, permission checks, stable errors without document values |
Public functions in these modules form the library API. JSON-LD processing accepts
a loader interface. Presentation verification accepts a ReplayStore implementation;
the CLI supplies SqliteReplay. Direct library callers must arrange appropriate
serialization for mutating key, status, and publication operations.
Credential issuance¶
- Parse input with duplicate-property and size checks, then validate the typed Holon.
- Select a schema ID and its full/disclosure profile. An optional
--schemaadds local validation; it does not replace the signed configured schema identity. - Load the suite and decrypt the key. Check algorithm, public fingerprint,
controller, verification method, key status, and
assertionMethodpurpose. - Resolve the issuer's did:web document and require authorization of the same key.
- Reject unapproved contexts, null values, and terms that would disappear during strict JSON-LD expansion. Build the unsigned VC with a new identifier.
- Reserve entries in the issuer's private status registry and add both revocation and suspension references.
- Sign using the selected real cryptosuite, then verify the result before output.
A failed issuance can leave an unused reserved status index. It is retained rather than recycled into another credential's identity. Publishing an output is an atomic file operation, not a transaction across every affected file.
Verification pipeline¶
flowchart LR
Input[Strictly parsed credential] --> Shape[VC structure and context policy]
Input --> Schema[Holon schema]
Input --> Dates[Validity and proof dates]
Shape --> DID[DID relationship authorization]
DID --> Signature[Cryptographic verification]
Shape --> Status[Signed status lists and cache TTL]
Schema --> Decision[Decision calculation]
Dates --> Decision
Signature --> Decision
Status --> Decision
Decision --> Authentic[Authentic assertion]
Authentic --> Trust[Matching scoped issuer policy]
Trust --> Evidence[Evidence integrity and explicit comparisons]
Evidence --> Report[Attributed decision with warnings and stage outcomes]
The diagram shows the logical stages, not parallel execution. Independent checks
are collected where safe; a stage that cannot run is marked not-evaluated rather
than treated as successful. An authorized DID key is not automatically trusted for
the Holon's claim type. Mandatory status failure is never interpreted as active.
Without a matching issuer policy, a credential that passes the required base checks
remains authentic-assertion. The first matching enabled policy in lexicographic
policy-ID order controls further requirements. Evidence must satisfy explicit
comparisons and configured organizational source groups to establish corroboration.
A digest match alone establishes integrity, not independent agreement.
Cryptographic adapters and disclosure¶
| Suite | Key | Document processing | Application role |
|---|---|---|---|
eddsa-rdfc-2022 |
Ed25519 | RDFC-1.0 and SHA-256 | Default credential, status, linkage and holder-authentication signing |
Ed25519Signature2020 |
Ed25519 | URDNA2015 and SHA-256 | Legacy verification and explicitly opted-in issuance |
ecdsa-sd-2023 |
P-256 | RDFC-1.0, HMAC labels, statement signatures and CBOR proof components | Base issuance, holder derivation and derived verification |
The modern adapters combine SSI JSON-LD selection/skolemization, Sophia canonical label mappings, and established RustCrypto/ed25519-dalek primitives. Resource limits cause rejection; there is no fallback to a weaker algorithm. Official vectors and independent bidirectional interoperability tests exercise this composition.
ECDSA-SD issuance creates an ephemeral signing key and HMAC label material for the base credential. Derivation first validates the base proof, selects the requested statements plus mandatory metadata, constructs the derived proof, and verifies it. It needs the base credential and public verification resources, not an issuer key. Stable identifiers, dates and status entries remain correlatable.
Ed25519 redaction uses a different operation: the original issuer signs a new
credential with a new ID, a new status allocation, and a reissuedFrom link. It is
not reported as holder derivation. Full and disclosure schema variants retain the
same signed HolonSubjectSchema reference.
Presentations and replay¶
An unsigned presentation transports already verified credentials and does not
authenticate a holder. A signed presentation adds a modern Ed25519
authentication proof binding the holder, expected challenge, domain, creation
time, and an expiry no more than five minutes later.
sequenceDiagram
participant V as Verifier
participant H as Holder
participant C as Verification service
participant R as Replay store
V->>H: Fresh unpredictable challenge and expected domain
H->>C: Signed presentation
C->>C: Validate every embedded credential
C->>C: Check holder authorization, signature, binding and expiry
C->>R: Atomically consume domain and challenge until expiry
alt First accepted use
R-->>C: Recorded
C-->>V: Report with holderAuthenticated true
else Already consumed
R-->>C: REPLAYED
C-->>V: Rejected report with holderAuthenticated false
end
SqliteReplay uses an immediate transaction and a unique digest of the
(domain, challenge) pair. A reopened store continues to reject reuse; concurrent
consumers cannot both accept the same pair. The verifier must preserve replay state
and generate fresh challenges. Holder authentication does not imply that the holder
is the credential subject.
Storage and publication¶
DATA/
├── keys/private/ encrypted secret envelopes
├── keys/public/ public key metadata
├── suites/ explicit suite bindings
├── trust/ local issuer policies
├── status/ private allocations and status registries
├── replay/ persistent challenge database
├── config/ TOML configuration and generated resource pins
└── site/ intentionally public issuer artifacts
├── .well-known/ discovery, linkage, JWKS and manifest
├── departments/.../ path-based DID documents, when applicable
└── status/ signed public status credentials
The documentation site built by Zensical goes to the separate repository directory
site-docs/; it does not contain the runtime DATA directory.
Private keys use Argon2id and XChaCha20-Poly1305 with authenticated metadata. Storage opens path components without following symlinks and checks ownership/permissions. Writes use temporary files, fsync, and rename. The CLI serializes its operations with a private directory lock. These controls do not protect against a compromised OS or another malicious process already running as the same user.
Public artifacts are written atomically per file, with the manifest committed last. That detects mixed generations during verification but does not make publication or rotation a single multi-file transaction. Deploy a complete versioned site directory and switch the server's release pointer when snapshot availability is required.
Network and configuration boundaries¶
The resolver checks HTTPS URLs, disables proxies/redirects/HTTP compression, checks all resolved addresses, pins DNS results for connection establishment, and verifies the connected address. DNS, connect, total request, and document-size limits bound resource use. Development access requires both the explicit CLI mode and a configured origin allowlist.
Pinned offline copies have content digests and expiries. Status pins additionally have retrieval timestamps checked against the signed TTL. Contexts use a separate local pinned loader; arbitrary network context loading and external schema references are denied. A resource pin is not an issuer-trust policy.
Read configuration, deployment, standards and limits, and the threat model for the operator-facing consequences. The requirements map identifies corresponding acceptance tests.