A certificate authority you can read.
One Go binary; the roles it plays are selected at start. Classical and post-quantum signatures are peers, not a bolt-on. The audit log is one you can prove, not merely read.
goca is a CA engine — the hardened core behind your RA/CLM, not a lifecycle portal.
Architecture
No externally reachable component can mint a certificate alone.
Issuance is split across four services with different authority.
api-cert records your request and asks the RA.
The RA decides against the template and signs its decision — with a
key it doesn't hold. The issuer verifies that signature with a public
key, builds the exact certificate bytes, lints them, and records a
purpose-bound intent. Only then does the key service sign — after
checking who's asking, what for, and whether an intent for exactly
those bytes exists.
the issuance split: api-cert → ra → issuer → keysvc
What a compromise buys an attacker
| Compromised component | What the attacker gains | What still blocks issuance |
|---|---|---|
api-cert |
Request traffic and its scoped principal | Cannot create an RA decision or call keysvc |
ra |
The ability to authorize within RA policy | Holds no signing key and no key-unwrapping secret; it can only ask keysvc to sign its decisions |
issuer |
Certificate construction and intent creation | Cannot forge the RA signature; keysvc enforces independent key policy |
keysvc |
Use of the custody assigned to that instance | Accepts only authenticated callers and recorded, purpose-bound intents; HSM/KMS policy can keep keys outside process memory |
Supply chain
A supply chain you can audit over coffee.
Exactly two direct Go dependencies. No frameworks, no ORM, no gRPC, no vendor SDKs. When the code that mints your certificates fits in one review, "trust us" becomes "read it".
Written in-house
ASN.1/DER encoding, AWS SigV4, QR codes, Shamir secret sharing, SPIFFE identity (own implementation, not SPIRE), and a certificate linter — hand-rolled, in the repo, readable.
cgo: 3 files
Native code is confined to three files — the PKCS#11 and Windows CNG bridges. Everything else is pure Go, including the cloud KMS clients.
No moving targets
Two dependencies is a diff you can actually read on update day. A dependency tree that fits on one screen is a security control, not an aesthetic.
Post-quantum
PQC as a peer, not a preview.
ML-DSA, SLH-DSA, and composite signatures sit next to ECDSA and RSA as equals — same templates, same custody model, same audit trail. You choose the algorithm; nothing else changes.
ML-DSA
Lattice-based signatures for everyday issuance — root, issuing CA, and end-entity alike.
SLH-DSA
Hash-based, conservative-assumption signatures — a common choice for long-lived roots.
Composite
Classical and post-quantum in one signature, for relying parties that need both to agree.
Strict-PKCS#11 ML-DSA — the private key never leaving the token — verified on real Utimaco Quantum Protect hardware, 2026-08-16. Not a simulator, not a software fallback.
Key custody
"HSM-backed" is a phrase that hides the difference.
A stored key's custody is one of three answers to a single question: where is the private key at the moment a signature happens? goca refuses to blur this — and so does this page.
| Mode | Where the key is when it signs | Signing rate | A stolen database gets an attacker |
|---|---|---|---|
software |
In the service's process memory, unwrapped under the KEK derived from your master secret | CPU speed | Every CA key — if they also have the master secret |
pkcs11-wrapped |
In keysvc process memory, unwrapped by the token, for the duration of the call |
CPU speed | Nothing usable — the wrapping key is on the token and is not extractable |
pkcs11 |
Inside the token. Every signature is one C_Sign |
Bounded by the appliance's signature rate | Nothing usable |
The ranking is pkcs11 > pkcs11-wrapped ≈
software, and it will keep being stated that way.
An audited acceptance gate
Wrapped custody isn't a checkbox: a backend earns it through an audited acceptance gate, and a Save button is test-gated — a backend is saved because a test proved it, not because you clicked.
16 vendor presets, runtime-pinned
Presets for 16 PKCS#11 vendors, including YubiKey PIV verified on real hardware. A runtime-pin catches the library being swapped out underneath you.
Cloud KMS in pure Go
AWS KMS, Azure Key Vault, and Google Cloud KMS clients with no vendor SDKs — hand-written request signing you can read, in the same custody vocabulary.
Windows CNG — labeled honestly
A CNG bridge exists in source; its device half is Windows-unverified — the bridge has not been compiled and run in the shipped image, and we won't imply otherwise.
Security controls
Controls that fail in the safe direction.
Offline TOTP with a compiled floor
MFA works with no internet, the admin minimum is compiled into the binary, and factors are tamper-evident.
Break-glass Shamir cards
Emergency access is split across physical cards — a quorum reconstructs it, no single card does.
Four-eyes that can't be deleted off
Deleting the four-eyes approval state turns the control on, not off.
Merkle audit, no retention knob
The audit log is a Merkle tree with signed checkpoints — there is no retention setting, and never will be.
Distroless, no shell
The container image has no shell, runs non-root on a read-only root filesystem — there is nothing to drop into.
Interfaces & protocols
What works today, and what doesn't yet.
Version 0.1.0. Features the manual marks "designed, not built" are labeled Roadmap here — never described as working.
| Interface | Status | Notes |
|---|---|---|
| REST API | Available | Console parity — everything the console does is a public REST call |
| ACME + ARI | Available | RFC 8555 issuance with renewal information |
| Admin console | Available | Docs embedded in the binary, served from the console |
| Notifications | Available | Expiry and lifecycle events |
| CRL distribution | Available | CRL-first revocation: issuance may stop; validation must not |
| Certificate templates | Available | 18 seeded, including eIDAS-profile templates (QWAC, QSeal, QSigC, PSD2) |
| External-parent CAs / adopt-existing-key | Available | Bring a CA signed elsewhere, or adopt a key that already exists |
| SPIFFE mesh identity | Available | Built in (own implementation); mTLS enforcement is staged — opt-in per listener, off by default, with a migration path |
| SCEP | Roadmap | No handler today |
| EST | Roadmap | No handler today |
| CMP | Roadmap | Listener shell only |
| OCSP | Roadmap | Designed; revocation is CRL-first today |
| Delta / sharded CRLs | Roadmap | Full CRLs today |
Deployment
Three shapes today. More designed.
Built Runs today
- Docker Compose all-in-one — one host, segmented networks per edge of the call graph
- Docker Compose distributed — the issuance split across containers and network segments
- Linux standalone — one binary under systemd, external PostgreSQL
Roadmap Designed, not built
- Appliance image
- Windows service / installer
- Offline-root kit — today:
externalParent: trueorNGCA_MODE=root-ca - Kubernetes / Helm packaging
Fifteen minutes to a verified CA.
goca is free during early access — download on request. Follow the quickstart, take the operator training, or ask the support assistant — it has read the entire manual and will tell you when the honest answer is "that isn't built yet".