aac-cli 0.1.5__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. aac_cli-0.2.0/PKG-INFO +781 -0
  2. aac_cli-0.2.0/README.md +764 -0
  3. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli/__init__.py +1 -1
  4. aac_cli-0.2.0/aac_cli/agent_cli.py +674 -0
  5. aac_cli-0.2.0/aac_cli/agent_health.py +420 -0
  6. aac_cli-0.1.5/aac_cli/workspace_layout.py → aac_cli-0.2.0/aac_cli/agent_layout.py +202 -121
  7. aac_cli-0.1.5/aac_cli/workspace_manifest.py → aac_cli-0.2.0/aac_cli/agent_record.py +48 -36
  8. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli/cli.py +4 -4
  9. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli/config_render.py +115 -72
  10. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli/dev_material.py +268 -6
  11. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli/init_cli.py +895 -368
  12. aac_cli-0.2.0/aac_cli/material_cases.py +175 -0
  13. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli/reference.py +41 -12
  14. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli/secure_files.py +1 -1
  15. aac_cli-0.2.0/aac_cli/supplied_material.py +293 -0
  16. aac_cli-0.2.0/aac_cli.egg-info/PKG-INFO +781 -0
  17. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli.egg-info/SOURCES.txt +6 -4
  18. {aac_cli-0.1.5 → aac_cli-0.2.0}/pyproject.toml +1 -1
  19. aac_cli-0.1.5/PKG-INFO +0 -537
  20. aac_cli-0.1.5/README.md +0 -520
  21. aac_cli-0.1.5/aac_cli/workspace_cli.py +0 -525
  22. aac_cli-0.1.5/aac_cli/workspace_health.py +0 -156
  23. aac_cli-0.1.5/aac_cli.egg-info/PKG-INFO +0 -537
  24. {aac_cli-0.1.5 → aac_cli-0.2.0}/LICENSE +0 -0
  25. {aac_cli-0.1.5 → aac_cli-0.2.0}/MANIFEST.in +0 -0
  26. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli/__main__.py +0 -0
  27. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli/admin_key_pem.py +0 -0
  28. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli/api_key_rotation_state.py +0 -0
  29. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli/config.py +0 -0
  30. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli/idp_recovery.py +0 -0
  31. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli/profiles.py +0 -0
  32. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli/registration_state.py +0 -0
  33. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli/sso_login.py +0 -0
  34. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli.egg-info/dependency_links.txt +0 -0
  35. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli.egg-info/entry_points.txt +0 -0
  36. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli.egg-info/requires.txt +0 -0
  37. {aac_cli-0.1.5 → aac_cli-0.2.0}/aac_cli.egg-info/top_level.txt +0 -0
  38. {aac_cli-0.1.5 → aac_cli-0.2.0}/setup.cfg +0 -0
aac_cli-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,781 @@
1
+ Metadata-Version: 2.4
2
+ Name: aac-cli
3
+ Version: 0.2.0
4
+ Summary: The aac platform CLI — headless operator interface to the AAC control plane (tenant registration, chain audit).
5
+ Author: Agent Authority Cloud Project
6
+ License-Expression: Apache-2.0
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: httpx>=0.28
11
+ Requires-Dist: cryptography>=42.0
12
+ Requires-Dist: PyYAML>=6.0
13
+ Provides-Extra: test
14
+ Requires-Dist: pytest>=8.0; extra == "test"
15
+ Requires-Dist: pytest-httpx>=0.30; extra == "test"
16
+ Dynamic: license-file
17
+
18
+ # aac-cli — the `aac` command
19
+
20
+ `aac` is the command-line tool for tenants of Agent Authority Cloud (AAC).
21
+ AAC lets an organisation's AI agents and services prove, on every request,
22
+ which tenant they belong to and what they were authorised to do. A
23
+ **tenant** is one organisation registered with AAC. Each of its workloads
24
+ runs beside an AAC sidecar that signs outgoing requests and verifies
25
+ incoming ones against public trust material the tenant publishes.
26
+
27
+ The `aac` command is how a tenant's operator does everything that is not
28
+ done by the sidecar itself: register the tenant, sign in, manage keys,
29
+ workloads and domains, and prepare a runnable local workload. Everything
30
+ works from a terminal or a script; there is no web console to click
31
+ through. Output is JSON by default, and every command that reports a
32
+ result also offers `--output table`.
33
+
34
+ Two kinds of tenant use the same tool:
35
+
36
+ * **Developer tenants** register themselves by signing in with GitHub or
37
+ Google. `aac init` takes a developer from an installed CLI to a runnable
38
+ local workload in one command.
39
+ * **Enterprise tenants** are registered through an onboarding ceremony with
40
+ AAC operations and sign in through their own identity provider. Their
41
+ keys, domains and workloads are managed with the `aac tenant` and
42
+ `aac sso` commands described further down.
43
+
44
+ ## Install
45
+
46
+ ```bash
47
+ pip install aac-cli
48
+ aac --version
49
+ ```
50
+
51
+ Python 3.10 or newer is required.
52
+
53
+ > **Mind the name.** `pip install aac` installs an unrelated project that
54
+ > happens to share the acronym. The AAC package is **`aac-cli`**; the
55
+ > command it installs is **`aac`**.
56
+
57
+ ## Guided setup for developer tenants: `aac init`
58
+
59
+ `aac init` prepares the AAC side of one of your agents on this machine: it
60
+ registers the agent's identity with AAC and creates the keys, certificates
61
+ and configuration its sidecar needs. The agent itself is yours. The first
62
+ run also creates your profile and registers your tenant; run it again with
63
+ a new `--agent` and `--workload-path`, and the same `--trust-url` and
64
+ `--idp`, for each further agent.
65
+
66
+ Along the way it signs you in, registers a developer tenant (or reuses the
67
+ one your profile is already bound to), takes the trust domain AAC assigns to the
68
+ tenant, registers a sample workload, generates development keys and
69
+ certificates, and writes a complete sidecar configuration together with the
70
+ files the trust-anchor publisher and Docker Compose need.
71
+
72
+ Point it at the endpoints your AAC environment gives you. The example below
73
+ uses the AAC stage environment:
74
+
75
+ ```bash
76
+ aac init --profile stage \
77
+ --admin-url https://api.stage.cascadeauth.dev \
78
+ --data-plane-url https://api.stage.cascadeauth.dev \
79
+ --trust-url https://trust.stage.cascadeauth.dev \
80
+ --display-name 'Example Team' --contact 'dev@example.com' --idp github
81
+ ```
82
+
83
+ The command shows what it will create and asks before registering the
84
+ tenant, because a tenant is permanent. In a script, where there is no
85
+ terminal to answer the question, pass `--create-tenant`:
86
+
87
+ ```bash
88
+ aac init --profile stage \
89
+ --admin-url https://api.stage.cascadeauth.dev \
90
+ --data-plane-url https://api.stage.cascadeauth.dev \
91
+ --trust-url https://trust.stage.cascadeauth.dev \
92
+ --display-name 'Example Team' --contact 'dev@example.com' --idp github \
93
+ --create-tenant
94
+ ```
95
+
96
+ `--idp` names how you sign in: `github` or `google`, the two sign-ins AAC
97
+ offers developer tenants. If you need another sign-in provider, contact
98
+ support@cascadeauth.com.
99
+
100
+ `--trust-url` is required when an agent is created: it is the public URL
101
+ where your AAC environment serves trust material, and for AAC stage it is
102
+ `https://trust.stage.cascadeauth.dev`. If you leave it out, the command
103
+ stops before creating anything and names the value your tenant already
104
+ uses, if it has one.
105
+
106
+ Setting up a new developer tenant takes two browser sign-ins: the first
107
+ registers the tenant, the second starts the tenant-admin session that
108
+ registers the workload. The command says so before the first one.
109
+
110
+ Progress lines `[1/5]` to `[5/5]` name the five steps: tenant, sign-in,
111
+ hosted trust domain, workload, and material; the settings files are
112
+ written last. Rerunning the
113
+ command is safe: it resumes an interrupted setup, reports a complete
114
+ agent, or names the exact conflict. It never overwrites private
115
+ material. The sign-in you chose with `--idp` is remembered, so a rerun
116
+ never asks you to pick between GitHub and Google again.
117
+
118
+ ### What it creates, and where
119
+
120
+ * `~/.aac/agents/<agent>/` holds the files **one sidecar** needs (default
121
+ name `starter`), in folders named for where each file goes. `sidecar/` is
122
+ what you mount or copy into the sidecar: the workload, receipt and HTTPS
123
+ key pairs, this agent's root-signing key, the CA certificate and the
124
+ outbound CA bundle. `agent/` is what the paired application reads: the
125
+ pairing secret and the CA certificate. `keep/` stays with you — it holds
126
+ the development CA private key, out of both mounts, so no container ever
127
+ sees the key that mints identities; an agent whose certificates your own
128
+ issuer signed has no `keep/` at all. Beside them sit
129
+ `sidecar-config.yaml`, `compose.env`, `state/` and `record.json`, which
130
+ lets a rerun resume.
131
+ * `~/.aac/tenants/<tenant-id>/` is **tenant-level** material shared by every
132
+ agent of that tenant: the tenant-admin signing key, the publisher's input
133
+ directories (`root-keys/` and `spiffe-bundle/`, public halves only) and
134
+ the generated `publisher.env`.
135
+ * `~/.aac/credentials/<tenant-id>` is the tenant API key. The generated
136
+ configuration references it by path.
137
+
138
+ **By default everything `aac init` generates is development material.** The
139
+ development CA lives seven days and the leaf certificates one day. Nothing
140
+ there qualifies for production, whatever the tenant's domain. If your own
141
+ issuer signs your agent's certificates instead, see "Two ways to get your
142
+ agent's certificates" below; moving to production also means a tenant-admin
143
+ key under production custody (`aac tenant rotate-admin-key`) and a root key
144
+ id published by your publisher.
145
+
146
+ ### What an agent folder is for, and how to use it
147
+
148
+ An agent folder holds everything one workload needs to run beside an AAC
149
+ sidecar, kept apart from every other workload: its identity in your
150
+ tenant's trust domain, its own root signing key and key id, its
151
+ certificates, the pairing secret its sidecar and application share, and the
152
+ settings files written for them. It also records how far setup got, so
153
+ rerunning `aac init` resumes where it stopped.
154
+
155
+ Use one agent per workload. `aac init --agent <name>` creates an agent
156
+ (with `--trust-url`) or resumes it (the default name is `starter`), and
157
+ three commands look after it:
158
+
159
+ ```bash
160
+ aac agent status --agent starter # what exists, what is due, and the next step
161
+ aac agent renew --agent starter # replace the short-lived certificates
162
+ aac agent list # every agent on this machine
163
+ ```
164
+
165
+ There is no separate command for the settings files. Running setup again —
166
+ with the profile the agent belongs to — writes `sidecar-config.yaml`,
167
+ `compose.env` and the tenant's `publisher.env` from what the agent already
168
+ records, without touching a single key:
169
+
170
+ ```bash
171
+ aac init --profile stage --agent starter # write them again
172
+ aac init --profile stage --agent starter --layout container # and change the layout
173
+ ```
174
+
175
+ That is how you rebuild a settings file you lost and how you move between
176
+ running the sidecar directly on this machine and running it in a container.
177
+ `--profile` is required because setup picks the profile before it reads the
178
+ agent; `aac agent status` prints the same command with the right profile
179
+ whenever it is the next step.
180
+
181
+ A second workload is a second agent with its own workload path:
182
+
183
+ ```bash
184
+ aac init --profile stage --agent worker --workload-path demo/worker \
185
+ --idp github --trust-url https://trust.stage.cascadeauth.dev
186
+ ```
187
+
188
+ Pass the same `--trust-url` and `--idp` as for your first agent. Each agent
189
+ records its own, and the trust URL also goes into the tenant's
190
+ `publisher.env`, which every agent of the tenant shares. Writing the
191
+ settings files never changes that file's endpoints, so an agent created with
192
+ a different trust URL stops at its last step and has to be set up again
193
+ under a new name.
194
+
195
+ `--agent` names the local files; `--workload-path` chooses the workload's
196
+ identity, the path at the end of its SPIFFE ID (`demo/agent` unless you
197
+ choose another). Two agents with the same workload path are the same
198
+ workload holding different keys: that is what a replacement computer wants,
199
+ and what a second workload does not. The agent name is permanent, because
200
+ the root key id is made from it. `aac init` needs the profile the agent was
201
+ created with; the `aac agent` commands read it from the agent itself.
202
+
203
+ ### Two ways to get your agent's certificates
204
+
205
+ Find your situation here and use the flags it lists. Nothing is inferred:
206
+ which case you are in is decided by whether you pass certificates your own
207
+ issuer signed, and the agent records the answer, so `aac agent status` can
208
+ tell you later and `aac agent renew` behaves accordingly.
209
+
210
+ <!-- material-cases:start -->
211
+
212
+ <!-- Generated from aac_cli/material_cases.py. Do not edit by hand. -->
213
+
214
+ #### The CLI creates a development CA
215
+
216
+ The laptop case. The CLI creates a certificate authority on this machine and signs the agent's identity, receipt and HTTPS certificates with it.
217
+
218
+ **Flags.** No flags are needed. To reuse a development CA across agents, pass `--ca-key-file` and `--ca-cert-file` together; neither one alone.
219
+
220
+ **Keys and signatures.** The CA and the two identity keys are Ed25519; the HTTPS key is EC P-256.
221
+
222
+ **The CLI** creates a 7-day certificate authority and keeps its private key in the agent's keep/ folder; issues the workload, receipt and HTTPS certificates; publishes the CA certificate as a trust anchor named `<agent>-dev-ca`.
223
+
224
+ **The CLI does not** ask you for anything from your own certificate authority.
225
+
226
+ **Renewal.** `aac agent renew --agent <name>` issues fresh certificates from the same CA.
227
+
228
+ #### I bring my own CA
229
+
230
+ The production case. Your own issuer has already signed the agent's certificates, and your CA private key never reaches this machine.
231
+
232
+ **Flags.** All seven together: `--workload-cert-file`, `--terminal-cert-file` and `--tls-cert-file`, each with its key file (`--workload-key-file`, `--terminal-key-file`, `--tls-key-file`), plus `--ca-cert-file`. Never `--ca-key-file`: the CLI does not want your CA key.
233
+
234
+ **Keys and signatures.** Your CA's key must be Ed25519 or EC P-256, and it must have signed each certificate with Ed25519 or ECDSA-with-SHA-256. The two identity keys may be Ed25519 or EC P-256; the HTTPS key must be EC P-256. RSA is not supported: the sidecar cannot verify against it.
235
+
236
+ **The CLI** checks each certificate against its key, its issuer and its validity window; registers the workload and writes the settings files and the pairing secret; publishes your CA certificate as a trust anchor named `<agent>-ca`.
237
+
238
+ **The CLI does not** create a certificate authority; issue any certificate; ask for, read or store your CA private key.
239
+
240
+ **Renewal.** `aac agent renew --agent <name>` cannot reissue what it did not sign: it takes the replacements your issuer produced.
241
+
242
+ <!-- material-cases:end -->
243
+
244
+ The settings files, `sidecar-config.yaml` and `compose.env` in the agent
245
+ folder and `publisher.env` in the tenant directory, follow from what the
246
+ agent records, in both cases. They hold endpoints, identifiers, settings and
247
+ the paths of the key files, never key material. `aac init` writes them as
248
+ its last step and writes them again on any later run. The layout decides the
249
+ paths written into the sidecar configuration: `host` (the default) uses
250
+ paths on this machine, for a sidecar that runs directly on it, and
251
+ `container` uses the paths where the files are mounted inside the
252
+ containers.
253
+
254
+ `publisher.env` belongs to the tenant, and every agent of the tenant writes
255
+ it. A write rewrites its paths and poll interval for this machine (hand
256
+ edits to those are replaced), but refuses to change its tenant ID, trust
257
+ domain or URLs, because that would repoint the publisher for every agent.
258
+ The refusal names the values that differ. If the file is the stale one, for
259
+ example after you deliberately moved the tenant to new endpoints and set up
260
+ its agents again, move it aside (`mv publisher.env publisher.env.old` in the
261
+ tenant directory) and run setup again from an agent with the intended
262
+ endpoints. The admin key and the published public keys stay where they are.
263
+
264
+ ### How profiles, agents and tenant data fit together
265
+
266
+ ```text
267
+ profile (~/.aac/config) ── bound to ──> tenant
268
+ ├── credentials: ~/.aac/credentials/<tenant-id>
269
+ ├── tenant directory: ~/.aac/tenants/<tenant-id>/
270
+ └── agents: ~/.aac/agents/<name>/, one per workload
271
+ ```
272
+
273
+ * A **profile**, in `~/.aac/config`, names the AAC admin and data-plane
274
+ endpoints you use and, once registration or sign-in has bound it, the one
275
+ tenant it acts for. The trust URL and the sign-in choice are recorded in
276
+ each agent instead. "Profiles and configuration", further down,
277
+ covers profiles in full.
278
+ * The **tenant data** is shared by all of a tenant's agents on this
279
+ machine: the tenant API key and the tenant-admin session in
280
+ `~/.aac/credentials/`, and the tenant directory
281
+ `~/.aac/tenants/<tenant-id>/` with the tenant-admin signing key, the public
282
+ keys and certificates the publisher uploads, and `publisher.env`.
283
+ * An **agent folder**, in `~/.aac/agents/<name>/`, holds one workload's
284
+ material. It records the profile and the tenant it was created for and
285
+ keeps them: one tenant can have many agents, and an agent never
286
+ moves to another tenant.
287
+
288
+ ### What each file is for, and what to back up
289
+
290
+ The table lists every file the CLI creates, what it is for, how long it
291
+ lives and whether to back it up. `<agent>` is the agent name
292
+ (default `starter`) and `<tenant-id>` the tenant id AAC allocated.
293
+
294
+ <!-- inventory-table:start -->
295
+ | No. | Material | Where | Used for | Lifetime | Back up? |
296
+ |---|---|---|---|---|---|
297
+ | 1 | tenant_api_key | `~/.aac/credentials/<tenant-id>` | CLI data-plane calls; the sidecar's workload projection | until retired or rotated; `aac tenant reissue-api-key` | **Yes.** save a protected copy in your secret manager; loss needs `aac tenant reissue-api-key` |
298
+ | 2 | tenant_admin_session | `~/.aac/credentials/<tenant-id>.session` | CLI admin calls | hours; `aac sso login` | No. sign in again |
299
+ | 3 | tenant_admin_signing_key | `~/.aac/tenants/<tenant-id>/tenant-admin.pem`, `~/.aac/tenants/<tenant-id>/tenant-admin.pub.pem` | the publisher signs trust-material uploads; the public half is registered with AAC | until rotated; `aac tenant rotate-admin-key` | **Recommended.** recommended for developer tenants (loss needs `aac tenant rotate-admin-key`; a lost public half is derived from the private key); production custody belongs in an HSM/KMS, not a password manager |
300
+ | 4 | root_signing_key | `~/.aac/agents/<agent>/sidecar/root.pem`, `~/.aac/agents/<agent>/sidecar/root.pub.pem`, `~/.aac/tenants/<tenant-id>/root-keys/<agent>-root-v1.pub.pem` | the sidecar mints authority chains; the public half is published under the key id | until you retire its key id (root key ids are fixed per agent name) | No. never restore the private key: a replacement agent gets a new name and therefore a new key id, published alongside the old one until the old one is retired; its public copies are recreated from it |
301
+ | 5 | development_ca | `~/.aac/agents/<agent>/keep/ca.key`, `~/.aac/agents/<agent>/sidecar/ca.crt`, `~/.aac/agents/<agent>/agent/ca.crt`, `~/.aac/tenants/<tenant-id>/spiffe-bundle/<agent>-dev-ca.ca.pem` | signs the workload, terminal and TLS certificates; peers trust it via the published bundle | 7 days; `aac agent renew --ca` | No. development only; reissue; a lost copy of the certificate is copied back from `sidecar/ca.crt` |
302
+ | 6 | workload_svid | `~/.aac/agents/<agent>/sidecar/workload.key`, `~/.aac/agents/<agent>/sidecar/workload.crt` | the sidecar's SPIFFE identity: proves on every live request that this workload holds the key behind its certificate | 1 day; `aac agent renew` | No. reissue |
303
+ | 7 | terminal_attestation | `~/.aac/agents/<agent>/sidecar/terminal.key`, `~/.aac/agents/<agent>/sidecar/terminal.crt` | signs the receipt (terminal attestation) the sidecar issues when this workload completes a delegated request; auditors verify it offline against your published CA; kept separate from the workload key so neither can forge the other's role | 1 day; `aac agent renew` | No. reissue |
304
+ | 8 | localhost_tls | `~/.aac/agents/<agent>/sidecar/server.key`, `~/.aac/agents/<agent>/sidecar/server.crt` | the sidecar's HTTPS listener | 1 day; `aac agent renew` | No. reissue |
305
+ | 9 | outbound_ca_bundle | `~/.aac/agents/<agent>/sidecar/outbound-ca.pem` | the sidecar verifies TLS to AAC and to itself | rebuilt by renew; `aac agent renew` | No. derived |
306
+ | 10 | pairing_secret | `~/.aac/agents/<agent>/agent/pairing.secret` | the sidecar and its agent authenticate each other (same bytes in both) | until regenerated | No. regenerable; both processes must read the same file |
307
+ | 11 | agent_record | `~/.aac/agents/<agent>/record.json` | the agent's identities and progress; every `aac agent` verb reads it first | for the life of the agent | **Recommended.** cannot be recreated by any command; without it start a new agent under a new name (see the new-computer setup procedure) |
308
+ | 12 | published_public_inputs | `~/.aac/tenants/<tenant-id>/root-keys`, `~/.aac/tenants/<tenant-id>/spiffe-bundle` | the publisher's input directories: every root public key and CA certificate this tenant has published, across all agents | for the life of the tenant | **Recommended.** back up with the tenant directory: a replacement agent must publish its new root key alongside a still-ACTIVE previous one (the control plane refuses a root-key set that drops every ACTIVE key at once) |
309
+ | 13 | settings_files | `~/.aac/agents/<agent>/sidecar-config.yaml`, `~/.aac/agents/<agent>/compose.env`, `~/.aac/tenants/<tenant-id>/publisher.env` | non-secret settings derived from the agent's record | written again on demand; `aac init --profile <profile> --agent <agent>` | No. derived; `aac init --profile <profile> --agent <agent>` writes them again |
310
+ <!-- inventory-table:end -->
311
+
312
+ In short: back up the tenant API key and the whole tenant directory while
313
+ the machine is healthy. Everything else is regenerated.
314
+
315
+ ### Inspect and maintain an agent
316
+
317
+ ```bash
318
+ aac agent status --agent starter --output table # what exists, expiry, permissions, next command
319
+ aac agent status --agent starter --remote # also: is the public trust material visible?
320
+ aac agent renew --agent starter # replace the short-lived certificates; old ones archived
321
+ aac agent list # every agent on this machine, with its case
322
+ aac init --profile stage --agent starter --layout container # write the settings again, container paths
323
+ ```
324
+
325
+ `status` works offline by default and exits with code 3 when material is
326
+ missing, expired or unsafely permissioned. `renew` reissues the one-day
327
+ certificates with fresh keys, and reissues the development CA only when it
328
+ is expired or within a day of expiry (or when `--ca` is given); a new CA is
329
+ published under the next anchor id and needs the publisher restarted. For an
330
+ agent whose own issuer signed its certificates, `renew` cannot reissue
331
+ anything: it takes the replacements your issuer produced, through the same
332
+ flags `aac init` accepts.
333
+ Re-running `aac init` writes the settings files again and never touches a
334
+ key.
335
+
336
+ `status` also names one next step, `next_command`. When material is
337
+ missing, it is the fix for the first missing item: restore it from your
338
+ backup, recreate a lost public copy from the file it copies, renew, or run
339
+ setup again. Otherwise it is renewal when a certificate is due. After the step,
340
+ run `status` again. Commands it names that change your tenant carry the
341
+ agent's own `--tenant-id` and `--admin-url`, so a value left in
342
+ `AAC_TENANT_ID` or `AAC_ADMIN_URL` cannot aim them at another tenant. The
343
+ file recipes use `openssl`, which must be OpenSSL 1.1.1 or newer: the
344
+ `openssl` that ships with macOS cannot read Ed25519 keys, so install a
345
+ current OpenSSL there, for example with Homebrew.
346
+
347
+ Every private file is created once, readable only by you, and never
348
+ overwritten. Before generating anything, `init` checks every destination
349
+ and refuses with the full list if one already exists. `aac init` never
350
+ installs or replaces a tenant-admin key for an existing tenant: the key on
351
+ this machine must already be the tenant's active admin key, and replacing
352
+ it is always the deliberate `aac tenant rotate-admin-key` command, never a
353
+ side effect of setup. A second agent for the same tenant
354
+ (`--agent other`) reuses the tenant-admin key and gets its own root key
355
+ id.
356
+
357
+ ### What to do when keys and certificates expire
358
+
359
+ It is the certificates that expire, and renewing replaces their keys with
360
+ them. `aac agent status` shows when each certificate runs out and flags
361
+ it before it does: six hours ahead for the one-day certificates, a day
362
+ ahead for the development CA.
363
+
364
+ | Material | Lasts | When it runs out | What to do |
365
+ |---|---|---|---|
366
+ | Workload, terminal-attestation and localhost TLS certificates | 1 day | the sidecar can no longer prove its identity, sign receipts or serve HTTPS | `aac agent renew --agent <name>`, then restart the sidecar so it loads them |
367
+ | Development CA | 7 days | certificates it signed stop being trusted, and no new ones can be issued | `renew` also replaces the CA when it is expired or within a day of expiry, or when `--ca` is given, and then reissues every certificate under it: restart the publisher so the new CA is published, restart the sidecar and anything else that trusted the old CA, and confirm with `aac agent status --agent <name> --remote` |
368
+ | Tenant-admin session | hours | administration commands ask you to sign in again | `aac sso login` |
369
+ | Tenant API key, tenant-admin signing key, root signing key | do not expire | — | replaced only on purpose: see "Keys" and "Setting up aac-cli on a new computer" |
370
+
371
+ A simple routine: when you start work, run
372
+ `aac agent status --agent <name>`, and when it names renewal, run
373
+ `aac agent renew --agent <name>` and restart the sidecar. About once
374
+ a week that renewal also replaces the development CA (`renew` then reports
375
+ `republication_required: true`): restart the publisher, the sidecar and the
376
+ agent, and anything else that trusted the old CA. Renewing is safe to
377
+ repeat, but it is not a no-op: every run issues fresh keys and moves the old
378
+ files to the agent's `archive/` directory, so whatever reads them must
379
+ reload. With the
380
+ [AAC Compose starter](https://github.com/CascadeAuth/aac-compose-starter),
381
+ `./starter up` recreates the containers so they load the new files; it
382
+ does not renew anything itself.
383
+
384
+ All of this is development material. In production, certificates come from
385
+ your own issuer and follow its renewal process.
386
+
387
+ ## Setting up aac-cli on a new computer
388
+
389
+ This procedure covers a lost machine and a second machine alike. It needs
390
+ the two backups named above: the tenant API key from
391
+ `~/.aac/credentials/<tenant-id>` and the tenant directory
392
+ `~/.aac/tenants/<tenant-id>/`. The tenant id is the `tnt-…` value in your
393
+ profile; `aac profile show` prints it, and the example below uses
394
+ `tnt-550e8400-e29b-41d4-9716-446655440000`.
395
+
396
+ 1. Install the CLI, create the profile, and sign in. Signing in needs the
397
+ tenant id, because a fresh profile is not yet bound to a tenant:
398
+
399
+ ```bash
400
+ pip install aac-cli
401
+ aac profile create stage \
402
+ --admin-url https://api.stage.cascadeauth.dev \
403
+ --data-plane-url https://api.stage.cascadeauth.dev
404
+ aac sso login --profile stage --idp github \
405
+ --tenant-id tnt-550e8400-e29b-41d4-9716-446655440000
406
+ ```
407
+
408
+ An enterprise tenant signs in through its own identity provider instead,
409
+ naming the connection by its URL:
410
+
411
+ ```bash
412
+ aac sso login --profile stage \
413
+ --idp-url https://login.microsoftonline.com/11111111-2222-3333-4444-555555555555/v2.0 \
414
+ --tenant-id tnt-550e8400-e29b-41d4-9716-446655440000
415
+ ```
416
+
417
+ 2. Restore the tenant API key to `~/.aac/credentials/<tenant-id>`, readable
418
+ only by you, or issue a new one if it was not backed up:
419
+
420
+ ```bash
421
+ aac tenant reissue-api-key --profile stage
422
+ ```
423
+
424
+ Restore `~/.aac/tenants/<tenant-id>/` from the backup, with directories
425
+ readable only by you and files likewise. Its `publisher.env` still names
426
+ the old machine's paths; step 3 rewrites them for this one.
427
+
428
+ If the tenant directory was not backed up, first check whether another
429
+ machine still holds `~/.aac/tenants/<tenant-id>/`: every machine that has
430
+ run `aac init` for this tenant has one. If so, copy it from there. That
431
+ is the backup, and the rest of this step does not apply.
432
+
433
+ If no copy exists anywhere, the tenant-admin key and every root key that
434
+ lived on the lost machine are gone for good. Install a new admin key,
435
+ then account for every root key the tenant has published (the `openssl`
436
+ commands need OpenSSL 1.1.1 or newer, which macOS does not ship):
437
+
438
+ ```bash
439
+ openssl genpkey -algorithm ed25519 -out tenant-admin.pem
440
+ openssl pkey -in tenant-admin.pem -pubout -out tenant-admin.public.pem
441
+ aac tenant rotate-admin-key --profile stage \
442
+ --tenant-admin-pubkey-file tenant-admin.public.pem
443
+ aac trust-anchor list --profile stage --role root-signing --output table
444
+ aac trust-anchor revoke --profile stage --kid starter-root-v1 \
445
+ --reason 'machine lost'
446
+ ```
447
+
448
+ The list shows each root key with its state. A publisher may drop a key
449
+ from its published set only while another active key stays in the set,
450
+ so a set holding nothing but the new key is accepted only once no other
451
+ key is active. Revoke every active key whose private half was lost, one
452
+ `revoke` per key id, and no other key: a root key that is still in use
453
+ elsewhere must stay active, and its public file must be in the tenant
454
+ directory the publisher runs from. Then run step 3 with
455
+ `--tenant-admin-key-file tenant-admin.pem` added.
456
+
457
+ 3. Run the guided setup with a **new agent name**. A developer tenant
458
+ passes its sign-in choice, so an expired session is renewed without a
459
+ prompt:
460
+
461
+ ```bash
462
+ aac init --profile stage --agent laptop-2 --idp github \
463
+ --trust-url https://trust.stage.cascadeauth.dev
464
+ ```
465
+
466
+ An enterprise tenant runs the same command without `--idp`; `--idp`
467
+ selects only the GitHub and Google developer sign-ins. The guided setup
468
+ then uses the session from step 1. If that session has expired, repeat
469
+ the `aac sso login --idp-url …` command from step 1 first:
470
+
471
+ ```bash
472
+ aac init --profile stage --agent laptop-2 \
473
+ --trust-url https://trust.stage.cascadeauth.dev
474
+ ```
475
+
476
+ 4. Start the publisher from the tenant directory, restored or written by
477
+ step 3. A restored directory's root-key set holds the old and the new
478
+ public keys, which is exactly what AAC requires: a set that drops every
479
+ currently active key at once is refused. Retire the old key later, by
480
+ removing its public file, once nothing signs with it. A directory written
481
+ fresh after the revocations in step 2 holds only the new key, which is
482
+ accepted because no other key is active any more.
483
+
484
+ ### Why the replacement agent needs a new name
485
+
486
+ The agent name is baked into two identifiers that are published for
487
+ the whole tenant: the root key id `<agent>-root-v1` and the CA
488
+ anchor id (`<agent>-dev-ca` for a development CA the CLI created,
489
+ `<agent>-ca` for your own issuer's). Other parties look up your keys and
490
+ certificates by those ids. AAC therefore never accepts new key material
491
+ under an id that has already been published: a key id names one key, for
492
+ good. A new machine has to generate new keys, so it has to publish them
493
+ under new ids, which means a new agent name. The new ids are published
494
+ alongside the old ones until the old ones are retired.
495
+
496
+ Reusing the old agent name on the new machine is refused on purpose:
497
+ the restored tenant directory still holds that name's published root key,
498
+ and `init` never generates different key material under an existing key
499
+ id. The development certificates and the pairing secret are regenerated by
500
+ step 3; they are never restored.
501
+
502
+ ## Profiles and configuration
503
+
504
+ A **profile** is a named set of endpoints plus the tenant the profile is
505
+ bound to. Profiles live in `~/.aac/config`, a plain INI file with one
506
+ section per profile:
507
+
508
+ ```ini
509
+ [main]
510
+ admin_url = http://127.0.0.1:8000
511
+ data_plane_url = http://127.0.0.1:9000
512
+ tenant_id = tnt-550e8400-e29b-41d4-9716-446655440000
513
+
514
+ [stage]
515
+ admin_url = https://api.stage.cascadeauth.dev
516
+ data_plane_url = https://api.stage.cascadeauth.dev
517
+ ```
518
+
519
+ ```bash
520
+ aac profile list # every profile, including the built-in main
521
+ aac profile show stage --output table # stored and effective values, with sources
522
+ aac profile create prod --admin-url https://aac-admin.example.com \
523
+ --data-plane-url https://aac-data.example.com
524
+ aac profile update main # edit the built-in baseline
525
+ aac profile delete prod # local only; never touches the server
526
+ ```
527
+
528
+ `main` is the reserved baseline profile. It always exists, with localhost
529
+ defaults until `aac profile update main` stores real values, and cannot be
530
+ created or deleted. Every operational command selects its profile in this
531
+ order: `--profile <name>`, then the `AAC_PROFILE` environment variable, then
532
+ `main`. A selected profile that does not exist is an error, never a silent
533
+ fallback. Within the selected profile, a flag beats an environment variable
534
+ (`AAC_ADMIN_URL`, `AAC_DATA_PLANE_URL`, `AAC_TENANT_ID`), which beats the
535
+ stored value.
536
+
537
+ `tenant_id` is managed for you. Profile commands display it but never ask
538
+ for it; only `aac tenant register`, `aac init` and `aac sso login` write it.
539
+ It is your organisation's identifier in the federation, allocated by AAC at
540
+ registration in the form `tnt-<uuid>`, and it never changes.
541
+
542
+ `AAC_CLI_HOME` relocates the whole `~/.aac` tree. Use it for a throwaway
543
+ test home, or to keep separate accounts hard-walled from each other:
544
+
545
+ ```bash
546
+ AAC_CLI_HOME=$HOME/.aac-prod aac profile list
547
+ ```
548
+
549
+ ### Credentials
550
+
551
+ Two credential files can coexist under `~/.aac/credentials/`:
552
+
553
+ * `<tenant-id>` holds the tenant API key, written once by registration and
554
+ printed exactly once. The sidecar and the data-plane commands
555
+ (`aac chain`, `aac trust-anchor`) present it. Only a hash of it exists
556
+ on the server, so a lost key is replaced, never recovered.
557
+ * `<tenant-id>.session` holds the short-lived session that `aac sso login`
558
+ creates. The administration commands (`aac tenant`, `aac sso`) use it
559
+ automatically. Sessions cannot be refreshed: when one expires, any
560
+ administration command tells you to sign in again, and there is no
561
+ refresh token that could be stolen.
562
+
563
+ ## Signing in
564
+
565
+ ```bash
566
+ aac sso login --profile stage --idp github # developer tenant: GitHub or Google
567
+ aac sso login --profile prod \
568
+ --idp-url https://login.microsoftonline.com/11111111-2222-3333-4444-555555555555/v2.0
569
+ aac sso login --profile prod \
570
+ --idp-url https://login.microsoftonline.com/11111111-2222-3333-4444-555555555555/v2.0 \
571
+ --flow pkce --no-browser # print the URL instead of opening a browser
572
+ aac sso whoami --profile stage --output table # cached session and the tenant's assigned domain
573
+ aac sso logout --profile stage
574
+ ```
575
+
576
+ A developer tenant signs in with `--idp github` or `--idp google`; if you
577
+ need another sign-in provider, contact support@cascadeauth.com. An
578
+ enterprise tenant signs in through its Microsoft Entra ID connection, named
579
+ by the connection's exact URL with `--idp-url`; when several connections
580
+ are registered, the error message lists the exact commands to choose one.
581
+
582
+ Sign-in uses the device flow by default, so the browser can be on another
583
+ machine and headless hosts work. Google connections use a loopback flow
584
+ instead, because Google's device flow is scope-restricted; `--flow`
585
+ overrides the choice. `whoami` and `logout` work offline and never contact
586
+ AAC.
587
+
588
+ ## Output and exit codes
589
+
590
+ Every command that reports a result prints one JSON document to standard
591
+ output by default, with progress notes and diagnostics on standard error,
592
+ so the JSON stays parseable in scripts; `--output table` prints a readable
593
+ table instead. The three profile writers are the exception: `aac profile
594
+ create`, `update` and `delete` confirm on standard error, leave standard
595
+ output empty and take no `--output`.
596
+
597
+ | Exit code | Meaning |
598
+ |---|---|
599
+ | `0` | Success. |
600
+ | `1` | AAC or the identity provider rejected the request. |
601
+ | `2` | Usage error: an invalid flag, value or flag combination. |
602
+ | `3` | A local configuration or state problem: profile, credential file, cached session or agent. |
603
+ | `4` | Transport failure: an endpoint could not be reached. |
604
+
605
+ If AAC asks you to slow down, the command shows the interval to wait. The
606
+ CLI never replays a change on its own; wait and rerun deliberately.
607
+
608
+ ## Enterprise tenants
609
+
610
+ Enterprise tenants are registered through an onboarding ceremony: AAC
611
+ operations opens a registration window and hands the tenant operator a
612
+ bootstrap token, which the commands below read from `--bootstrap-token` or
613
+ the `AAC_BOOTSTRAP_TOKEN` environment variable. The token authorises only
614
+ the onboarding steps shown with it; every other administration command runs
615
+ under a signed-in session.
616
+
617
+ For enterprise sign-in, AAC supports Microsoft Entra ID (also known as
618
+ Azure Active Directory or Azure AD). If your organisation needs another
619
+ identity provider, contact support@cascadeauth.com.
620
+
621
+ ### Register the tenant
622
+
623
+ Generate the tenant-admin key pair first. The CLI transmits the public half
624
+ only and refuses a private key:
625
+
626
+ ```bash
627
+ openssl genpkey -algorithm ed25519 -out tenant-admin.pem
628
+ openssl pkey -in tenant-admin.pem -pubout -out tenant-admin.public.pem
629
+ aac profile create prod --admin-url https://aac-admin.example.com \
630
+ --data-plane-url https://aac-data.example.com
631
+ aac tenant register --profile prod --display-name 'Example Corporation' \
632
+ --contact ops@example.com \
633
+ --workload-spiffe-id spiffe://example.com/treasury-agent/v1 \
634
+ --tenant-admin-pubkey-file ./tenant-admin.public.pem \
635
+ --bootstrap-token "$AAC_BOOTSTRAP_TOKEN"
636
+ ```
637
+
638
+ AAC allocates the tenant id; you never choose one. The response prints the
639
+ id and the tenant API key exactly once, and the CLI saves both. A profile
640
+ that is already bound to a tenant refuses to register another; use an
641
+ unbound profile. If the response is lost, rerun the same command with no
642
+ changes to the identifying flags: the CLI resumes the frozen request rather
643
+ than registering twice.
644
+
645
+ ```bash
646
+ aac tenant describe --profile prod --output table
647
+ aac tenant update --profile prod --tenant-id tnt-550e8400-e29b-41d4-9716-446655440000 \
648
+ --display-name 'Example Corporation Ltd'
649
+ ```
650
+
651
+ ### Connect your identity provider
652
+
653
+ An identity-provider connection is described by a JSON file
654
+ (`aac sso register-idp --help` shows the fields, with an example for a
655
+ Microsoft Entra ID tenant). Before the first
656
+ connection, generate an offline recovery key so the connection can be
657
+ repaired if the provider is ever unavailable; only the enrollment file
658
+ leaves the machine:
659
+
660
+ ```bash
661
+ aac sso generate-idp-recovery-key \
662
+ --private-key-file offline-idp-recovery-private.pem \
663
+ --enrollment-file idp-recovery-enrollment.json
664
+ aac sso enroll-idp-recovery-key --profile prod \
665
+ --file idp-recovery-enrollment.json --bootstrap-token "$AAC_BOOTSTRAP_TOKEN"
666
+ aac sso register-idp --profile prod --file connection.json \
667
+ --bootstrap-token "$AAC_BOOTSTRAP_TOKEN"
668
+ aac sso list-idp --profile prod
669
+ ```
670
+
671
+ Later connections, corrections (`aac sso replace-idp`) and the two-party
672
+ repair ceremony (`aac sso request-idp-repair`, `aac sso approve-idp-repair`)
673
+ are described by their `--help`.
674
+
675
+ ### Keys
676
+
677
+ Replacing the tenant-admin key is immediate and forward-only: the previous
678
+ key stops verifying at once and can never be reinstated. The trust-anchor
679
+ publisher signs its uploads with this key and loads it once, at start, so
680
+ sequence the change: stop the publisher, register the new public half, put
681
+ the new private key where the publisher's configuration points, start the
682
+ publisher, and confirm that its first upload is accepted under the new key.
683
+ A publisher installed as a system service reads the key path from its
684
+ environment file, which you can point at the new file. A Docker publisher's
685
+ environment and key mount are fixed when the container is created, so
686
+ overwrite the key at the mounted path and recreate the container; a plain
687
+ restart would come back on the old key.
688
+
689
+ ```bash
690
+ aac tenant rotate-admin-key --profile prod \
691
+ --tenant-admin-pubkey-file tenant-admin.public.pem
692
+ ```
693
+
694
+ A developer tenant set up with `aac init` keeps its tenant-admin key in its
695
+ tenant directory, where the publisher's configuration points. To replace a
696
+ lost key there, write the new pair in place, readable only by you, sign in,
697
+ register the new public half, and then recreate every publisher container.
698
+ Pass the tenant id and admin URL explicitly, as `aac agent status`
699
+ does, so the change cannot land on another tenant. The `openssl` commands
700
+ need OpenSSL 1.1.1 or newer, which macOS does not ship:
701
+
702
+ ```bash
703
+ cd ~/.aac/tenants/tnt-550e8400-e29b-41d4-9716-446655440000
704
+ (umask 077 && openssl genpkey -algorithm ed25519 -out tenant-admin.pem.tmp && mv tenant-admin.pem.tmp tenant-admin.pem)
705
+ (umask 077 && openssl pkey -in tenant-admin.pem -pubout -out tenant-admin.pub.pem.tmp && mv tenant-admin.pub.pem.tmp tenant-admin.pub.pem)
706
+ aac sso login --profile stage --idp github \
707
+ --tenant-id tnt-550e8400-e29b-41d4-9716-446655440000 \
708
+ --admin-url https://api.stage.cascadeauth.dev
709
+ aac tenant rotate-admin-key --profile stage \
710
+ --tenant-id tnt-550e8400-e29b-41d4-9716-446655440000 \
711
+ --admin-url https://api.stage.cascadeauth.dev \
712
+ --tenant-admin-pubkey-file tenant-admin.pub.pem
713
+ ```
714
+
715
+ The tenant API key can be rotated without downtime. `issue` stages a second
716
+ active key in a new file and never replaces the current one; move every
717
+ client to it, then retire the old key by its exact id:
718
+
719
+ ```bash
720
+ aac tenant api-key list --profile prod
721
+ aac tenant api-key issue --profile prod
722
+ aac tenant api-key retire --profile prod --key-id ak_0123456789abcdef --yes
723
+ ```
724
+
725
+ A lost or suspected-compromised key is replaced instead: every active key is
726
+ revoked and one fresh key is printed once.
727
+
728
+ ```bash
729
+ aac tenant reissue-api-key --profile prod
730
+ ```
731
+
732
+ ### Domains and workloads
733
+
734
+ Every tenant gets a hosted trust domain from AAC; registration saves it in
735
+ the profile, and `aac tenant assign-hosted-domain` assigns one to a tenant
736
+ that has none yet. To use your own DNS domain as a trust domain instead,
737
+ prove control of it with a DNS TXT record, then bind it:
738
+
739
+ ```bash
740
+ aac tenant issue-domain-challenge --profile prod --domain example.com
741
+ # Publish the TXT record the command prints, then:
742
+ aac tenant verify-domain --profile prod --domain example.com
743
+ aac tenant bind-trust-domain --profile prod --trust-domain example.com
744
+ aac tenant list-trust-domains --profile prod --output table
745
+ ```
746
+
747
+ Workloads are the identities your sidecars run under:
748
+
749
+ ```bash
750
+ aac tenant add-workload --profile prod --spiffe-id spiffe://example.com/payroll/v1 \
751
+ --display-name Payroll
752
+ aac tenant list-workloads --profile prod
753
+ aac tenant deactivate-workload --profile prod --workload-id wl-2f9c1e --reason 'service retired'
754
+ ```
755
+
756
+ Deactivation is terminal, and a deactivated SPIFFE id stays reserved.
757
+
758
+ ### Inspect trust material and audit a chain
759
+
760
+ ```bash
761
+ aac trust-anchor list --profile prod --output table # every published key, every lifecycle state
762
+ aac trust-anchor describe --profile prod --kid starter-root-v1
763
+ aac chain show --profile prod \
764
+ --token-id c3d5e7f9c3d5e7f9c3d5e7f9c3d5e7f9c3d5e7f9c3d5e7f9c3d5e7f9c3d5e7f9 --output table
765
+ aac chain show --profile prod \
766
+ --token-id c3d5e7f9c3d5e7f9c3d5e7f9c3d5e7f9c3d5e7f9c3d5e7f9c3d5e7f9c3d5e7f9 \
767
+ --render --render-out ./chain.html
768
+ ```
769
+
770
+ `chain show` accepts any hop's token id or the chain root id and shows the
771
+ chain to any tenant that took part in it. The timeline carries metadata
772
+ only; the business content of each step stays in your own audit stream.
773
+
774
+ ## Getting help
775
+
776
+ `aac --help` lists the command groups and `aac <command> --help` shows every
777
+ flag with its default. Errors name the exact command to run next.
778
+
779
+ ## License
780
+
781
+ Apache-2.0.