Examples
Configuration examples.
goca is configured as documents: the create-CA form is the
POST /v1/cas body, and every other object is a versioned
resource behind one uniform API. The console holds each document in a
split view — the form on one side, the same document as YAML or JSON
on the other — so every example on this page is also exactly what you
would click together in the console.
YAML or JSON — the same document
The control below switches every example between the two. They are one document in two representations, not two features: the API speaks JSON, so the JSON view is the literal request body, and the YAML view is that same document in the form the console's code pane holds it — and the form a reviewed change lives in before it is applied. Comments are a YAML-only luxury, so nothing on this page hides inside one: whatever is worth saying is said in the prose beside the block.
These examples target the current build
Verified against the product manual on 2026-08-17. Where a
field is stored but not yet enforced (for example an endpoint's
sourceRanges), the example leaves it out rather than
pretending. Free — download on request.
ECC issuing CA (ECDSA P-384)
One POST /v1/cas creates the CA, and the optional
spec block sets labels, path length, CRL cadence and the
publication URLs at creation time — before the first certificate can be
issued without them.
POST /v1/cas
YAML
name: ica-tls
commonName: ACME TLS Issuing CA G1
algorithm: ecdsa-p384
validityYears: 10
issuerRef: acme-root
spec:
labels:
family: tls
env: prod
constraints:
pathLen: 0
crl:
full:
interval: 24h
validity: 72h
onRevocation: immediate
publication:
baseUrl: http://pki.acme.example
rollover:
mode: manual
JSON
{
"name": "ica-tls",
"commonName": "ACME TLS Issuing CA G1",
"algorithm": "ecdsa-p384",
"validityYears": 10,
"issuerRef": "acme-root",
"spec": {
"labels": {
"family": "tls",
"env": "prod"
},
"constraints": {
"pathLen": 0
},
"crl": {
"full": {
"interval": "24h",
"validity": "72h"
},
"onRevocation": "immediate"
},
"publication": {
"baseUrl": "http://pki.acme.example"
},
"rollover": {
"mode": "manual"
}
}
}
Key generation is executed by keysvc in the selected custody —
the management process never receives a CA private key.
Post-quantum root CA (SLH-DSA)
No issuerRef means a self-signed root;
slh-dsa-sha2-192s is the manual's conservative PQC root
choice (ECDSA P-384 the classical one).
POST /v1/cas
YAML
name: root-pqc
commonName: Example Root CA G1
organization: Example
algorithm: slh-dsa-sha2-192s
validityYears: 20
JSON
{
"name": "root-pqc",
"commonName": "Example Root CA G1",
"organization": "Example",
"algorithm": "slh-dsa-sha2-192s",
"validityYears": 20
}
For production hierarchies, prefer running CA creation as a ceremony (plan → quorum approval → execute); the direct API path creates immediately and is fine for evaluation.
ACME endpoint (RFC 8555)
An AcmeEndpoint document publishes a directory at
https://<head>/acme/<name>/directory; allowed
templates are advertised as ACME profiles, registration defaults to
external account binding, and order retention lives on the endpoint
because the endpoint is what produced the orders. The config API wraps
every document in a spec field; the console's code pane
shows what is inside it.
PUT /v1/config/resources/AcmeEndpoint/web
YAML
spec:
enabled: true
defaultTemplate: tls-server-ecdsa
templates: [tls-server-ecdsa, tls-server-pqc]
registration:
mode: eab-required
challenges: [http-01, dns-01]
retention:
orders: 720h
JSON
{
"spec": {
"enabled": true,
"defaultTemplate": "tls-server-ecdsa",
"templates": ["tls-server-ecdsa", "tls-server-pqc"],
"registration": {
"mode": "eab-required"
},
"challenges": ["http-01", "dns-01"],
"retention": {
"orders": "720h"
}
}
}
Endpoint lint requires automatic approval on the bound templates — ACME
clients expect prompt order completion. Set
GOCA_ACME_BASE_URL to the external origin clients use.
TLS server template → CA binding
Which CA issues under a template is declared on the template
(issuerRef) — endpoints bind a template and the CA follows,
so the binding is maintained in exactly one place; the seeded TLS server
templates ship unbound by design, so the first save picks the CA. When the
same profile must issue from several CAs, derive instead of copying:
derivedFrom names the master, and issuerRef is
the only thing a derived document is allowed to change.
POST /v1/templates
YAML
name: tls-server-pqc-dev
document:
derivedFrom: tls-server-pqc
issuerRef: ica-tls-dev
JSON
{
"name": "tls-server-pqc-dev",
"document": {
"derivedFrom": "tls-server-pqc",
"issuerRef": "ica-tls-dev"
}
}
Those two fields are the derived template's entire document —
anything else is rejected ("edit the master instead"). The save carries
the name beside it: in the console the name is the field above the editor
and the code pane holds only the document half; over the API
the two travel in one body. A derived template mirrors the master's
latest version at resolution time. Known caveat in the current build:
derived templates carry a registered defect (build-gap register CA-011) —
check the register before relying on them.
YubiKey PIV as a PKCS#11 module
A Pkcs11Module document names the vendor library, the token
and a PIN reference — never the PIN itself; this exact custody
path is verified on real YubiKey hardware.
presenceRequired: true is the touch policy: unattended
signing is refused, not retried.
PUT /v1/config/resources/Pkcs11Module/yubikey-piv
YAML
spec:
displayName: YubiKey PIV
library: /usr/lib/x86_64-linux-gnu/libykcs11.so
tokenLabel: "YubiKey PIV #19923655"
keyLabelPrefix: goca-
presenceRequired: true
login:
mode: pin
pinRef: secret:hsm-pin
JSON
{
"spec": {
"displayName": "YubiKey PIV",
"library": "/usr/lib/x86_64-linux-gnu/libykcs11.so",
"tokenLabel": "YubiKey PIV #19923655",
"keyLabelPrefix": "goca-",
"presenceRequired": true,
"login": {
"mode": "pin",
"pinRef": "secret:hsm-pin"
}
}
}
pinRef is a reference — the document is versioned, returned
verbatim by GET and copied into the audit chain, so a PIN
typed there is a PIN published. Note the quotes on
tokenLabel: a # after a space starts a YAML
comment, so a label carrying one has to be quoted. A YubiKey PIV applet
permits one session, so serve it from exactly one keysvc
(bind the module to a site only that keysvc proves, or name
it in that keysvc's GOCA_PKCS11_MODULE allowlist). A
touch-policy key suits an offline root, not an issuing CA with scheduled
CRLs.
AWS KMS provider
A KmsProvider document names the region and an auth mode.
instance-role — IRSA, an ECS task role or an EC2 instance
profile — stores no credential anywhere and is the production choice;
GOCA_KMS_PROVIDER on keysvc selects the
document.
PUT /v1/config/resources/KmsProvider/aws-prod
YAML
spec:
provider: aws-kms
region: eu-central-1
auth:
mode: instance-role
JSON
{
"spec": {
"provider": "aws-kms",
"region": "eu-central-1",
"auth": {
"mode": "instance-role"
}
}
}
Off AWS compute there is no instance role, so the same document names a static key instead: the key ID inline, because it is an identifier that rides in every signed request, and the secret access key by reference — never the value.
PUT /v1/config/resources/KmsProvider/aws-onprem
YAML
spec:
provider: aws-kms
region: eu-central-1
auth:
mode: access-key
accessKeyId: AKIAIOSFODNN7EXAMPLE
secretAccessKeyRef: secret:aws-kms-secret
JSON
{
"spec": {
"provider": "aws-kms",
"region": "eu-central-1",
"auth": {
"mode": "access-key",
"accessKeyId": "AKIAIOSFODNN7EXAMPLE",
"secretAccessKeyRef": "secret:aws-kms-secret"
}
}
}
AKIAIOSFODNN7EXAMPLE is AWS's own documentation placeholder,
not a live key. AWS and Google Cloud KMS are built and accepted by the
validator, but the console's provider editor currently offers Azure only —
apply an AWS or GCP document through the API as above. A PQC
(ML-DSA/SLH-DSA) CA on any cloud provider uses kms-wrapped
custody; strict kms on AWS signs classical algorithms up to
P-521.
MFA policy and four-eyes approval
The MFA floor is compiled in — system-admin always requires a
second factor and no document can turn that off; an
MfaPolicy may only add roles.
PUT /v1/config/resources/MfaPolicy/default
YAML
spec:
requiredRoles: [system-admin, ca-officer]
JSON
{
"spec": {
"requiredRoles": ["system-admin", "ca-officer"]
}
}
Multi-person approval counts people including the proposer, so four eyes
is people: 2. Turning it on is a command rather than a
document, so it has no YAML view in the console and none here:
POST /v1/approval/enable
JSON
{
"people": 2,
"scope": "security"
}
JSON only — a command, not a versioned document.
Enablement refuses until at least people + 1 administrators
are eligible, and once it is on you cannot turn it off alone — only a full
quorum, or the break-glass cards. Deleting the approval state turns the
control on, not off.
Try them against a running instance.
The fifteen-minute Docker Compose quickstart gives you a CA these documents can be applied to.
Request a download link Quickstart
Free — download on request.