Configure the sidecar
Start the AAC journey · All references
This reference is for a deployment or integration you have chosen to configure. For the first authenticated reservation, use the packaged Compose journey above.
One tenant with many production agents
This section explains the production deployment model. The public developer-beta binary license permits evaluation and integration development with stage; it does not grant production or third-party service use.
Register the tenant once, establish its administrator and any enterprise IdP connection/recovery path, and protect the tenant-admin signing key. Publish the tenant's CA certificates and the root public keys of agents that start tasks. The CLI user guide provides the developer and enterprise registration procedures.
For each agent, register its workload, obtain a matching SPIFFE certificate/key from your issuer chaining to the published CA, prepare its sidecar config, private pairing secret and scoped tenant API key, and deploy the pair with restricted ingress and appropriate persistent state. Give a root-signing key only to originators and a receipt-signing key to agents that finish tasks.
For a chain of 100 agents where agent n calls agent n+1, first select the
existing tenant's prod profile and active assigned or custom trust domain.
Replace YOUR_ACTIVE_TRUST_DOMAIN with that domain, then register the workloads:
AAC_TRUST_DOMAIN='YOUR_ACTIVE_TRUST_DOMAIN'
for n in {1..100}; do
aac tenant add-workload \
--profile prod \
--spiffe-id "spiffe://$AAC_TRUST_DOMAIN/agent-$n" \
--display-name "Agent $n"
done
Issue the corresponding certificates through your tenant issuer. Configure agents 1–99 with their actual successor's HTTPS endpoint and exact workload audience, and agent 100 to return the terminal receipt. Agent 1 also needs its initial class of action. Forwarding narrows the received authority and signs with the forwarder's identity key. This is a configuration example, not a 100-agent capacity measurement or an automatic deployment command.
| Agent 1 (starts the task) | Agents 2–100 (receive and forward) | |
|---|---|---|
| The sidecar | yes | yes |
| An identity: a SPIFFE ID with a certificate and key from the tenant's own issuer, chaining to the CA the tenant publishes | yes | yes |
The workload registered with AAC (aac tenant add-workload --spiffe-id …, one per agent, scriptable) |
yes | yes |
| A root signing key (mints the chain's first authority) | yes | no — a forwarding sidecar narrows the authority it received and signs its proof with its own identity key |
| A receipt-signing (terminal attestation) key | if it finishes tasks | agent 100, which finishes the task |
| The sidecar config, the pairing secret shared with its agent, the tenant API key its sidecar uses to reach AAC | yes | yes |
Once per tenant: registration/admin key and coordinated public trust publishing. For this chain only agent 1's root public key needs publishing. All 100 agents need identities; receiving and forwarding does not require an originator key.
The CLI's supplied-material configuration workflow can validate and arrange your issuer's certificate/key files. It does not become a production CA or provision remote hosts. For role-specific minimal deployments use the configuration template and key inventory: do not generate unused originator/terminal keys merely to fill a generic example. Protect and deliver private material only to its actual consumer, then verify authenticated calls and receipts before admitting traffic.
Publisher ownership with multiple domains
One root-key writer owns the tenant's root set. One SPIFFE writer owns each active binding. A single process can serve both roles for one domain; another domain requires a separate publisher process with its own public-CA directory.
| Setting / authority | Publisher A | Publisher B |
|---|---|---|
| Tenant ID | Same tenant | Same tenant |
| SPIFFE trust domain | AAC-assigned domain | Proven custom domain |
| SPIFFE bundle directory | Assigned-domain public CA anchors | Custom-domain public CA anchors |
| SPIFFE ingest/read URLs | Ingest URL and assigned-domain read URL | Ingest URL and custom-domain read URL |
| Root-key role | Root public-key directory and ingest URL | Both AAC_TAP_ROOT_KEYS_DIR and AAC_TAP_ROOT_KEYS_INGEST_URL unset |
Both use authorized tenant-admin signing credentials and discover their own active binding version. Root sequences are tenant-scoped; SPIFFE sequences are binding-episode scoped. Never start competing writers for either scope. The publisher receives public root/CA material and its administration credential, never a CA private key or workload signing private key. Adding a custom domain does not rename existing workload identities; register and issue new identities deliberately while retaining any hosted workloads you still need.
Installation artifacts inventory
Choose one sidecar installation format: the container for Docker/Kubernetes, or a standalone binary for a VM, systemd host or macOS. The remaining artifacts serve tenant administration, public-trust publication and workload integration. They do not all need to be installed on every workload host. For the trust anchor publisher, choose one deployment option as well: its Docker container or Python package. Both run the same daemon. Run one writer for your tenant's root keys and one for each active domain binding, reusing an existing publisher where available: a tenant with a single trust domain needs one process, and a tenant that keeps its assigned domain and adds its own runs two, with root-key publishing enabled in exactly one of them.
The package pages and downloads below are public. Installing these artifacts does not require a GitHub, Docker Hub or PyPI account. Registering and managing your AAC tenant does require the sign-in described in Register your developer tenant below.
| Artifact | Package or registry location | Type | When to install |
|---|---|---|---|
| AAC CLI | aac-cli on PyPI | Command-line administration tool | Used by the tenant operator for this guide's onboarding, credentials, public trust and trace commands. The running sidecar does not depend on the CLI. |
| AAC Sidecar container (default) | docker.io/cascadeauth/aac-sidecar | Ready-made Linux container image | Choose this for Docker/Kubernetes. This and the standalone binary below are alternative installations of the same sidecar. |
| Trust anchor publisher Docker container | ghcr.io/cascadeauth/aac-trust-anchor-publisher | Containerized public-trust publisher | Choose this for a container host or orchestrator to publish/manage the tenant's public root keys and SPIFFE CA bundle. Docker or the orchestrator manages its lifecycle. |
| Trust anchor publisher Python package (alternative) | aac-trust-anchor-publisher on PyPI | Python wheel that installs the publisher daemon command | Choose this for installation in a host/VM's Python virtual environment; use systemd on a managed Linux host where applicable. It requires Python, unlike the sidecar's compiled standalone binary. |
| AAC Sidecar standalone bundle (alternative) | oras pull --output ./aac-sidecar-bundle docker.io/cascadeauth/aac-sidecar:v0.4.4-bundle |
Download containing standalone binaries, guide/template and audit evidence | Choose this if you are not using the container. Install ORAS, then follow the standalone installation steps. Container users can download the template directly from this site. |
| Invoke authentication | aac-invoke-auth on PyPI; optional [fastapi] extra |
Framework-independent Python signing/verification library, with an optional FastAPI/Starlette adapter | Install it in a Python workload that uses these helpers. Use [fastapi] for the supplied middleware/dependency integration or the protocol reference's Python example. Other stacks need compatible pairing authentication; they do not need to install this Python package. |
Image verification does not require the standalone bundle. The container
signature is attached to its registry image and can be checked directly with
Cosign, followed by a pull of the verified digest. The bundle adds standalone
binaries and the SPDX/provenance/OCI files used by the optional deep audit.
ORAS is a command-line tool for downloading files stored in a container
registry. The command above saves the bundle's files in ./aac-sidecar-bundle;
use a new, empty directory. Use oras pull for these files and docker pull
for the runnable sidecar image. ORAS is not needed to run the sidecar.
Installing current releases
Use the current release of each AAC artifact. The sidecar installation commands below use the current published version. The image and standalone bundle use the same version. Exact versions and digests are available in the release record.
The sidecar has no latest tag, so an untagged pull will fail and every sidecar
command here names the version. Its companion packages are different: install
the current release of each from PyPI without a version pin, and let pip resolve
it —
aac-cli— the operator's command lineaac-trust-anchor-publisher— publishes the tenant's public trust materialaac-invoke-auth— verifies sidecar pairing signatures in a Python application
Upgrade an existing companion installation before following this guide. Pin a version only when your own deployment needs a fixed one.
For the advanced verification path, reject a tag that does not resolve to a SHA-256 digest or an artifact whose signature or digest fails.
Downloading, copying, installing, or using the sidecar accepts the included
AAC Sidecar Developer Beta Binary License 1.0. The three Python companion
artifacts remain independently licensed under Apache-2.0. The bundle and image
also carry THIRD_PARTY_NOTICES.md for modules linked into the compiled binary.
AAC stage endpoints
Use https://api.stage.cascadeauth.dev for both CLI admin and data-plane
requests. Public trust documents are served from
https://trust.stage.cascadeauth.dev. These are AAC stage endpoints, not a
production service or an instruction to expose your sidecar's loopback port.
Check HTTPS reachability without credentials or tenant creation:
curl --fail --silent --show-error https://api.stage.cascadeauth.dev/healthz
The response includes status: ok and control_plane_version. This is a
liveness check, not proof that your tenant, trust material, or sidecar is ready.
Supported deployments and endpoint allowlist
| Surface | Supported beta profile |
|---|---|
| Container | Linux amd64/arm64; ready-made distroless image; UID/GID 65532 |
| Standalone | Linux amd64/arm64 and macOS amd64/arm64; non-root process |
| Agent pairing | Same network namespace; local /invoke and /a2a/v1 authenticated with the per-pair secret |
| Native A2A | A2A 1.0 unary SendMessage; JSON-RPC 2.0; no streaming or general-purpose A2A method support |
| Root and terminal signing | File-backed Ed25519/P-256, or explicit Azure Key Vault Standard software-protected non-exportable P-256 keys |
| Workload DPoP | Local file-backed key matching the SVID; remote DPoP is unsupported |
| Replay | Basic: explicit memory/basic, process-local and lost on restart. Shared durable: qualified authenticated-TLS Valkey ha-retained-write-safe; no fallback |
| A2A retry state | Retained private bbolt file, one process owner; storage must be qualified for your deployment |
| Service level | Developer evaluation/integration beta; no production SLA or production-rate claim |
The sidecar is provider-neutral. AWS/GCP/HSM/PKCS#11 signer adapters, general plugin loading, and tenant-built sidecar images are outside this beta profile. Azure hosting is optional; use the qualification worksheet below if relevant.
Allow only the endpoints your selected installation actually needs:
| Caller | Destination | Purpose and boundary |
|---|---|---|
| Installer | Docker Hub registry/auth/content endpoints for docker.io/cascadeauth/aac-sidecar |
Public image and bundle download; no AAC artifact credential |
| Installer | pypi.org, files.pythonhosted.org |
Public Python companion/sample dependencies |
| Publisher container installer | ghcr.io/cascadeauth/aac-trust-anchor-publisher and GHCR's content endpoints |
Optional public publisher image |
| Advanced verifier | Sigstore's public verification/transparency services as required by Cosign | Validate release identity and transparency evidence |
| CLI, sidecar projection/telemetry, publisher | https://api.stage.cascadeauth.dev:443 |
Admin/data APIs, STS, signed trust ingest; use the appropriate credential role |
| Sidecar, publisher public reads | https://trust.stage.cascadeauth.dev:443 |
Public root-key and SPIFFE-bundle polling |
| CLI/browser | Selected GitHub/Google sign-in endpoints and CLI's temporary local callback | Interactive identity-provider sign-in; no blanket IdP access needed by the sidecar |
| Paired agent/client | 127.0.0.1:8080; sidecar to 127.0.0.1:8000 |
Trusted local APIs/callbacks; do not expose outside the shared namespace |
| Peer sidecars | Explicit configured peer HTTPS endpoints, normally port 9443 | TLS, AAC chain, DPoP and recipient verification; URLs must be final, without redirects |
| Sidecar, if Shared durable selected | Tenant-local authenticated-TLS Valkey endpoint | Shared retained replay claims; no fallback to Basic or central service |
| Optional Azure signer | Exact tenant vault HTTPS hostname and platform managed-identity endpoint | Managed identity plus pinned key-version get/sign; no client secret or file fallback |
Registry/CDN and identity-provider redirects are operated by those providers; apply their current endpoint policy to installer/browser hosts. They are not a reason to allow general internet egress from the running sidecar. Your DNS, time synchronization and PKI distribution must also work. The local sample uses only loopback peer endpoints and the explicit stage trust/data hosts.
Configuration and workflow state
Keep business progress and reports in your application's storage. Messages waiting for other branches of a workflow are buffered temporarily and can be lost when the sidecar restarts. Replay protection and retry-result storage do not replace an application database.
For work spanning days or weeks, the tenant application must retain its own
business evidence and progress, then obtain independently authorized fresh
chains at checkpoints. It may explicitly reuse a task_ref for correlation.
Stored evidence does not renew expired authority; AAC does not supply a workflow
database or automatic long-running orchestration.
You can configure request timeouts in your sidecar YAML when an agent or peer needs more or less time to respond:
| Tenant setting | Use it to |
|---|---|
timeouts.agent_invoke_timeout_seconds |
Limit how long the sidecar waits for your local agent |
timeouts.cross_org_dispatch_timeout_seconds |
Set the default timeout for a request to another sidecar |
destinations.<name>.timeout_ms |
Override that default for one destination, in milliseconds |
Use positive, unquoted numbers. A destination override takes precedence over the dispatch default. For A2A, keep it within the configured overall operation deadline. Leave these settings at their defaults unless your application needs an adjustment.
Set authority duration with valid_for on the class or destination. Business
dates in the payload do not extend that authority or the request timeout.
Optional Azure qualification worksheet
Complete this before relying on the Azure adapter or a particular storage class. The local example does not supply these results. Keep identifiers and sanitized receipts in your own tenant record; never send keys to AAC.
| Check | Record and pass condition |
|---|---|
| Exact package | Image/bundle digest, version, signature identity and guide checksum |
| Root/terminal keys | Separate, exact versioned HTTPS key URIs; Standard software-protected P-256; public keys match configured identity/certificates |
| Managed identity | Selected system/user-assigned identity and least-privilege key get/sign; local workload DPoP stays local |
| Failures | Disabled key, removed grant, auth failure, throttling, timeout, malformed/wrong-key response: no minted artifact and no fallback |
| Persistent storage | Provider/SKU/class/mount options; private ownership; remount and replacement retain bbolt; missing/corrupt/insecure/locked files fail closed |
| Capacity | Allocate storage for your expected traffic and retention period, including saved responses. Confirm records survive replacement and that capacity limits produce a clear, recoverable failure. |
| A2A requests | Check that your normal request sizes and response times fit your configured limits. Confirm retries do not repeat a completed business action. |
| Connectivity | Public trust polling, exact workload projection, central metadata delivery and local terminal evidence |
| Lifecycle | Credential rotation/revocation, upgrade, rollback, rejected-beta handling, local cleanup and retained-state custody |
| Cost/support | Your expected cloud costs, responsible operator and escalation contact |
When using signers, configure each purpose with provider: azure-key-vault
and an exact versioned key_uri; omit that purpose's file-backed private-key
setting. Keep the terminal certificate and workload SVID/key. Set
AZURE_CLIENT_ID only when selecting a user-assigned managed identity.
Unsupported providers or conflicting file/provider settings fail startup.
Advanced installation guide — container
For optional signature and digest checks, use the artifact verification.
1. Place the sidecar beside the workload
The image already contains the compiled /aac-sidecar entrypoint and runs as
non-root 65532:65532. Do not copy the binary into a tenant-built image.
The production security boundary expects the sidecar and its paired agent to
share one network namespace. In Kubernetes, put both containers in the same
Pod. The agent listens on 127.0.0.1:8000; the sidecar keeps
sidecar.agent_invoke_url: http://127.0.0.1:8000/invoke and its loopback API on
127.0.0.1:8080. Expose only the sidecar external port 9443 through the
tenant's approved Service/ingress path.
Mount rather than bake:
/etc/aac/sidecar-config.yaml— reviewed configuration, mode0600;- TLS certificate/key and outbound CA material;
- the per-pair invoke-auth secret, read-only in both containers;
- tenant trust and SPIFFE material;
- any file-backed root/terminal signing material; and
/var/lib/aacon operator-qualified persistent storage for bbolt state.
The starter selects Basic replay protection. Choose Shared durable when replay history must survive restart or coordinate replicas; provision the qualified Valkey service and workload credentials first. Both profiles require the normal identity, trust, TLS and pairing setup. See replay profiles.
A minimal Pod fragment is:
apiVersion: v1
kind: Pod
metadata:
name: paired-agent
spec:
securityContext:
runAsNonRoot: true
fsGroup: 65532
containers:
- name: agent
image: <tenant-agent-image-by-digest>
env:
- name: AAC_INVOKE_AUTH_SECRET_FILE
value: /etc/aac/invoke-auth/invoke-auth.secret
volumeMounts:
- name: invoke-auth
mountPath: /etc/aac/invoke-auth
readOnly: true
- name: aac-sidecar
# Use the immutable image digest from the published component record.
image: docker.io/cascadeauth/aac-sidecar@sha256:REPLACE_WITH_SELECTED_DIGEST
args: ["-config", "/etc/aac/sidecar-config.yaml"]
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
ports:
- name: aac-external
containerPort: 9443
volumeMounts:
- name: sidecar-config
mountPath: /etc/aac
readOnly: true
- name: invoke-auth
mountPath: /etc/aac/invoke-auth
readOnly: true
- name: aac-state
mountPath: /var/lib/aac
volumes:
- name: sidecar-config
secret:
secretName: aac-sidecar-config
defaultMode: 0400
- name: invoke-auth
secret:
secretName: aac-invoke-auth
defaultMode: 0400
- name: aac-state
persistentVolumeClaim:
claimName: aac-sidecar-state
Set your tenant's TLS, trust, replay, signer, resource and ingress settings before deploying. Download sidecar-config.template.yaml, fill every required path and identifier, and verify replay, trust and an authenticated workflow before admitting traffic.
Plain Docker may use --network=container:<agent-container> to share the
agent's loopback namespace. Ordinary Docker Compose containers have separate
loopback interfaces; widening the sidecar loopback bind is a development-only
posture and requires dev_mode: true, so it is not the production recipe.
2. Verify process identity and readiness
docker exec aac-sidecar /aac-sidecar -version
curl --fail --silent http://127.0.0.1:8080/healthz
curl --fail --silent http://127.0.0.1:8080/readyz
Run the two HTTP checks from the shared Pod/network namespace. Do not expose
the loopback port. Liveness is not readiness: keep admission closed unless
/readyz confirms replay-authority readiness, then independently verify trust
material and an authenticated workflow.
Companion developer tools
Install the AAC CLI on the operator's machine in a dedicated virtual environment:
python3 -m venv .aac-tools
. .aac-tools/bin/activate
python -m pip install --upgrade aac-cli
aac --version
aac profile --help
aac tenant register --help
aac tenant api-key --help
aac trust-anchor --help
If this host will operate the tenant's publisher using Python, install it here. Skip this wheel install if you use its container deployment or an existing tenant-operated publisher:
python -m pip install --upgrade aac-trust-anchor-publisher
aac-trust-anchor-publisher --help
Install the following in the Python agent's environment for the protocol reference's optional FastAPI example or your own FastAPI/Starlette integration. It can use the same environment for the local example:
python -m pip install --upgrade 'aac-invoke-auth[fastapi]'
python -m pip show aac-invoke-auth
aac-invoke-auth is a library, not a separate daemon. Its base package supplies
framework-independent Python helpers; another Python framework can use those
without the [fastapi] extra. Other workload stacks must provide equivalent
pairing authentication before trusting sidecar requests. Installing this Python
package is conditional; authenticating the paired calls is not.
For deployments with separate ingest and public trust hosts, set
AAC_TAP_SPIFFE_BUNDLE_READ_URL to the full public SPIFFE bundle URL supplied
for the trust domain. For stage that is
https://trust.stage.cascadeauth.dev/.well-known/spiffe-bundle/<trust-domain>.
Signed uploads continue to use the supplied API-host ingest URL.
Create a local stage profile after installing the CLI:
aac profile create stage \
--admin-url https://api.stage.cascadeauth.dev \
--data-plane-url https://api.stage.cascadeauth.dev
This writes local profile settings only; it does not register a tenant. If a
profile named stage already exists, inspect it before changing it. Continue with developer self-service below, or use your existing
enterprise tenant and approved credential process.
Never paste API keys, recovery keys, signing
keys, pairing secrets, private payloads, or complete configuration into support
messages.
Register your developer tenant
Prefer aac init for supported tenant registration and agent setup. It prepares
the tenant-admin key, assigned domain, workload, certificates and configuration
in CLI-managed directories. Start with the CLI user guide
for shared GitHub, shared Google and enterprise Microsoft Entra ID onboarding;
the enterprise first connection and recovery verifier are prerequisites to its
normal init flow. The complete command reference
describes the latest supported released CLI.
Save the guide's agent YAML for your workload, then run its aac init command. Generated CA/leaf material is development-only. The CLI also validates supplied certificates and keys from your issuer; that choice alone does not qualify a production deployment. Use the public reservation demo for an executed two-tenant example with no user-written inline Python.
Optional advanced manual registration
The following lower-level route is for operators who deliberately manage the
material paths themselves; it is not required by the guided setup or demo.
set -euo pipefail is Bash error handling. Here it makes the private-key
existence guard stop execution rather than overwrite an existing key.
Choose a new, unbound profile for each tenant. The following example uses the
stage profile created above and GitHub sign-in; use --idp google for Google.
Your verified identity becomes the first tenant administrator.
Generate a tenant-admin key in a new private directory. The publisher needs this key to sign ingest requests; it is distinct from the workload's root key. Do not rerun key generation over an existing key.
set -euo pipefail
export AAC_PROFILE=stage
export AAC_MATERIAL_DIR="$HOME/aac-material/$AAC_PROFILE"
umask 077
mkdir -p "$AAC_MATERIAL_DIR"
test ! -e "$AAC_MATERIAL_DIR/tenant-admin.pem"
openssl genpkey -algorithm ed25519 -out "$AAC_MATERIAL_DIR/tenant-admin.pem"
openssl pkey -in "$AAC_MATERIAL_DIR/tenant-admin.pem" \
-pubout -out "$AAC_MATERIAL_DIR/tenant-admin.pub.pem"
aac tenant register --profile "$AAC_PROFILE" \
--display-name 'YOUR TEAM OR PROJECT' --contact 'YOUR EMAIL' \
--tenant-admin-pubkey-file "$AAC_MATERIAL_DIR/tenant-admin.pub.pem" \
--idp github --output table
aac sso login --profile "$AAC_PROFILE"
aac sso whoami --profile "$AAC_PROFILE" --output table
AAC_TENANT_ID=$(aac profile show "$AAC_PROFILE" --field tenant-id)
export AAC_TENANT_ID
aac tenant describe --profile "$AAC_PROFILE" --output table
Follow the CLI's browser/device instructions. AAC assigns the tnt-<uuid>
identifier; there is no user-chosen --tenant-id on registration. The CLI
shows the API-key value once, stores it with mode 0600 under
~/.aac/credentials/<tenant-id>, and binds the profile. Save a protected copy
in your secret manager. A bound profile refuses a second registration; create
a different profile for a different tenant. If a registration response is
interrupted, use the same profile and follow the CLI's resume instruction;
do not create a competing registration to recover the response.
Bind your trust domain and register the workload
AAC assigns your tenant a trust domain, so you need no DNS name and no TXT record. Assigning it and registering a workload are separate operations; run them in this order:
AAC_TRUST_DOMAIN=$(aac tenant assign-hosted-domain --profile "$AAC_PROFILE" --field trust-domain)
export AAC_TRUST_DOMAIN
echo "$AAC_TRUST_DOMAIN"
aac tenant list-trust-domains --profile "$AAC_PROFILE" --output table
aac tenant add-workload --profile "$AAC_PROFILE" \
--spiffe-id "spiffe://${AAC_TRUST_DOMAIN}/demo/agent" --display-name 'Synthetic demo agent'
aac tenant list-workloads --profile "$AAC_PROFILE" --output table
The assigned domain is built from your tenant identifier and looks like
tnt-<uuid>.tenants.stage.cascadeauth.dev. Registration itself usually assigns
it already and prints it, so the command above normally reads that same domain
back; it is idempotent and safe to rerun. If a previous hosted binding was
revoked, reactivate it with aac tenant reactivate-hosted-domain before
registering a workload. Workload registration records the identity only; your
tenant's PKI/SPIFFE system still issues its certificate and matching private
key.
Use a DNS domain of your own instead
Optional, and only if your tenant must be identified by its own name, such as
agents.example.com. It needs a DNS name you control and a published TXT
record. Use your actual canonical lowercase DNS name:
export AAC_TRUST_DOMAIN=agents.example.com
aac tenant issue-domain-challenge --profile "$AAC_PROFILE" --domain "$AAC_TRUST_DOMAIN"
Publish the exact TXT name/value returned by that command at your DNS provider,
wait for propagation, then bind it and register the workload under it. Run these
lines rather than the block above, whose first line would replace
AAC_TRUST_DOMAIN with the assigned domain:
aac tenant verify-domain --profile "$AAC_PROFILE" --domain "$AAC_TRUST_DOMAIN"
aac tenant bind-trust-domain --profile "$AAC_PROFILE" --trust-domain "$AAC_TRUST_DOMAIN"
aac tenant add-workload --profile "$AAC_PROFILE" \
--spiffe-id "spiffe://${AAC_TRUST_DOMAIN}/demo/agent" --display-name 'Synthetic demo agent'
aac tenant list-workloads --profile "$AAC_PROFILE" --output table
Issuing a new challenge replaces the prior challenge; retain the current TXT record while the binding needs domain evidence. Existing or previously revoked bindings follow their lifecycle rules, so an ownership/history rejection needs operator resolution. Do not retry it with a bootstrap token or invent a tenant ID.
Keep credential roles separate
Every key, certificate and shared secret this guide creates is described in one place: Keys and certificates. That page says what each item proves, where it lives, who gets a copy and how long it lasts, and it covers both ways an agent gets its certificates — a development authority created for you, or your own issuer's. Read it before you generate anything.
Two rules the rest of this section depends on. Use Ed25519 or P-256 keys and
currently valid, matching SPIFFE certificates; your CA bundle must validate the
workload and terminal certificates for the bound domain. And keep pairing secrets
distinct from every signing key: for a managed deployment, generate one in a new
protected location with umask 077; openssl rand -hex 32 > invoke-auth.secret,
then install it privately in both processes. The development recipe below
generates its own.
Optional development integration fixture
The manual PKI fixture is only for the optional protocol example. Normal setup uses the CLI.
Publish public trust material
For a file-backed demonstration, create a root-signing key and public half in your private onboarding directory, then copy only the public half into the publisher's root-key directory:
export AAC_ROOT_KEY_ID=demo-root-v1
test ! -e "$AAC_MATERIAL_DIR/${AAC_ROOT_KEY_ID}.pem"
openssl genpkey -algorithm ed25519 -out "$AAC_MATERIAL_DIR/${AAC_ROOT_KEY_ID}.pem"
openssl pkey -in "$AAC_MATERIAL_DIR/${AAC_ROOT_KEY_ID}.pem" \
-pubout -out "$AAC_MATERIAL_DIR/${AAC_ROOT_KEY_ID}.pub.pem"
mkdir -p "$AAC_MATERIAL_DIR/root-public"
cp "$AAC_MATERIAL_DIR/${AAC_ROOT_KEY_ID}.pub.pem" "$AAC_MATERIAL_DIR/root-public/"
export AAC_TAP_TENANT_ID="$AAC_TENANT_ID"
export AAC_TAP_ADMIN_KEY_FILE="$AAC_MATERIAL_DIR/tenant-admin.pem"
export AAC_TAP_ROOT_KEYS_DIR="$AAC_MATERIAL_DIR/root-public"
export AAC_TAP_ROOT_KEYS_INGEST_URL=https://api.stage.cascadeauth.dev/v1/root-keys/ingest
export AAC_TAP_POLL_INTERVAL_SECONDS=60
aac-trust-anchor-publisher
This runs the installed publisher in the foreground. Run one active writer for
your tenant's root keys and one per active domain binding, and never two
competing writers for the same root set or binding, to avoid uncoordinated
ingest sequence changes. The public filename
stem is the root key ID; configure tenant.key_id: demo-root-v1 and mount the
private half only into the workload. Rotation gets a new key ID.
Successfully ingested public material remains stored after the publisher exits;
the serving cache TTL is not a key-expiry timer. Run the publisher again when
keys or CA bundles change, following their rotation/revocation lifecycle.
To enable SPIFFE-bundle publication, stop the foreground publisher, place
public CA certificates only in a separate directory using filenames
<anchor_id>.ca.pem, and add these settings in the same shell before
restarting that one publisher. The trust-domain binding must already be active.
export AAC_TAP_SPIFFE_BUNDLE_DIR="$AAC_MATERIAL_DIR/spiffe-ca-public"
# Populate this directory with your issuer's public <anchor_id>.ca.pem files.
export AAC_TAP_SPIFFE_TRUST_DOMAIN="$AAC_TRUST_DOMAIN"
export AAC_TAP_SPIFFE_BUNDLE_INGEST_URL=https://api.stage.cascadeauth.dev/v1/spiffe-bundle/ingest
tap_trust_url=https://trust.stage.cascadeauth.dev
export AAC_TAP_SPIFFE_BUNDLE_READ_URL="${tap_trust_url}/.well-known/spiffe-bundle/${AAC_TRUST_DOMAIN}"
aac-trust-anchor-publisher
For the optional integration reference's development PKI, set AAC_TAP_SPIFFE_BUNDLE_DIR to
"$AAC_DEMO_DIR/publish-ca" instead. That directory contains only the public
development CA certificate. Never point the publisher at pki/.
Check the public bundle at the configured read URL and root keys at
https://trust.stage.cascadeauth.dev/.well-known/aac-root-keys/<tenant-id>.
Do not send a CA private key or workload private key to the publisher.
In another terminal, confirm ingest and public key visibility:
aac trust-anchor ingest-history --profile "$AAC_PROFILE" --output table
aac trust-anchor list --profile "$AAC_PROFILE" --output table
Only proceed to the runnable example when the expected root key and SPIFFE CA bundle are visible, your workload projection is registered, and all configured key/certificate pairs are valid. A successful registration by itself does not complete this preparation.