aac-cli 0.2.8__tar.gz → 0.2.10__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 (60) hide show
  1. {aac_cli-0.2.8/aac_cli.egg-info → aac_cli-0.2.10}/PKG-INFO +106 -17
  2. {aac_cli-0.2.8 → aac_cli-0.2.10}/README.md +104 -15
  3. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/__init__.py +1 -1
  4. aac_cli-0.2.10/aac_cli/__main__.py +13 -0
  5. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/aeg_cli.py +5 -0
  6. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/agent_cli.py +19 -6
  7. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/agent_health.py +21 -4
  8. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/cli.py +230 -54
  9. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/config.py +145 -35
  10. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/config_render.py +20 -4
  11. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/init_cli.py +3 -3
  12. aac_cli-0.2.10/aac_cli/interrupts.py +48 -0
  13. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/material_cases.py +1 -1
  14. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/profiles.py +114 -59
  15. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/reference.py +13 -4
  16. {aac_cli-0.2.8 → aac_cli-0.2.10/aac_cli.egg-info}/PKG-INFO +106 -17
  17. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli.egg-info/SOURCES.txt +1 -0
  18. aac_cli-0.2.10/launchers/aac +36 -0
  19. aac_cli-0.2.10/launchers/aeg +33 -0
  20. {aac_cli-0.2.8 → aac_cli-0.2.10}/pyproject.toml +3 -3
  21. aac_cli-0.2.8/aac_cli/__main__.py +0 -6
  22. aac_cli-0.2.8/launchers/aac +0 -32
  23. aac_cli-0.2.8/launchers/aeg +0 -29
  24. {aac_cli-0.2.8 → aac_cli-0.2.10}/LICENSE +0 -0
  25. {aac_cli-0.2.8 → aac_cli-0.2.10}/MANIFEST.in +0 -0
  26. {aac_cli-0.2.8 → aac_cli-0.2.10}/NOTICE +0 -0
  27. {aac_cli-0.2.8 → aac_cli-0.2.10}/THIRD_PARTY_NOTICES.md +0 -0
  28. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/__init__.py +0 -0
  29. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/cli.py +0 -0
  30. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/data/action-taken-v1.json +0 -0
  31. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/data/predicates-v1.json +0 -0
  32. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/data/renderer-source.json +0 -0
  33. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/from_sidecar.py +0 -0
  34. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/inputs.py +0 -0
  35. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/model.py +0 -0
  36. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/narrative.py +0 -0
  37. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/provenance.py +0 -0
  38. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/schemas.py +0 -0
  39. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/temporal.py +0 -0
  40. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/theme.py +0 -0
  41. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/timeline_source.py +0 -0
  42. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/visualizer.py +0 -0
  43. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/admin_key_pem.py +0 -0
  44. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/aeg_list.py +0 -0
  45. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/agent_config.py +0 -0
  46. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/agent_layout.py +0 -0
  47. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/agent_record.py +0 -0
  48. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/api_key_rotation_state.py +0 -0
  49. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/chain_list.py +0 -0
  50. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/dev_material.py +0 -0
  51. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/idp_recovery.py +0 -0
  52. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/registration_state.py +0 -0
  53. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/secure_files.py +0 -0
  54. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/sso_login.py +0 -0
  55. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/supplied_material.py +0 -0
  56. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/trace_query.py +0 -0
  57. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli.egg-info/dependency_links.txt +0 -0
  58. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli.egg-info/requires.txt +0 -0
  59. {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli.egg-info/top_level.txt +0 -0
  60. {aac_cli-0.2.8 → aac_cli-0.2.10}/setup.cfg +0 -0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aac-cli
3
- Version: 0.2.8
3
+ Version: 0.2.10
4
4
  Summary: The aac platform CLI — headless operator interface to the AAC control plane (tenant registration, chain audit).
5
5
  Author: Agent Authority Cloud Project
6
6
  License-Expression: Apache-2.0
7
- Project-URL: Documentation, https://cascadeauth.github.io/aac-starter-guide/cli/
7
+ Project-URL: Documentation, https://docs.cascadeauth.com/cli/
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.11
10
10
  Classifier: Programming Language :: Python :: 3.12
@@ -34,12 +34,13 @@ which tenant they belong to and what they were authorised to do. A
34
34
  runs beside an AAC sidecar that signs outgoing requests and verifies
35
35
  incoming ones against public trust material the tenant publishes.
36
36
 
37
- New to AAC? [What AAC is for](https://cascadeauth.github.io/aac-starter-guide/#what-aac-is-for)
37
+ New to AAC? [What AAC is for](https://docs.cascadeauth.com/overview/#what-aac-is-for)
38
38
  explains what delegated authority between agents buys you, and what it does not.
39
39
 
40
- The [CLI user guide and complete command reference](https://cascadeauth.github.io/aac-starter-guide/cli/)
40
+ The [CLI user guide and complete command reference](https://docs.cascadeauth.com/cli/)
41
41
  cover developer and enterprise onboarding, everyday administration and recovery.
42
- Select documentation matching `aac --version`.
42
+ These pages describe the latest supported release. Upgrade the CLI to use the
43
+ documented commands, and check your installation with `aac --version`.
43
44
 
44
45
  The `aac` command is how a tenant's operator does everything that is not
45
46
  done by the sidecar itself: register the tenant, sign in, manage keys,
@@ -77,7 +78,7 @@ Python 3.11 to 3.14 is supported. Release checks test all four versions.
77
78
  ## Use `aeg` to view agent execution graphs
78
79
 
79
80
  Installing or upgrading `aac-cli` supplies both `aac` and `aeg`.
80
- Use the [AEG guide and command reference](https://cascadeauth.github.io/aac-starter-guide/aeg.html)
81
+ Use the [AEG guide and command reference](https://docs.cascadeauth.com/aeg/)
81
82
  for listing local and central observations, rendering evidence, and selecting a profile.
82
83
 
83
84
  `aeg list --profile PROFILE --since 7d` discovers participant-visible central
@@ -111,7 +112,7 @@ Google developer sign-in. See “Setting up aac-cli on a new computer” below
111
112
  for the existing-tenant sign-in and material preparation steps.
112
113
 
113
114
  Setup uses the tenant's assigned trust domain and writes the files the
114
- trust-anchor publisher and Docker Compose need, as well as the sidecar
115
+ trust anchor publisher and Docker Compose need, as well as the sidecar
115
116
  configuration. By default it generates development keys and certificates;
116
117
  the certificate choices below explain how to supply your own instead.
117
118
 
@@ -341,7 +342,7 @@ and destination maps allow at most 128 entries each.
341
342
  | `destinations.<name>.audience_pattern` | Required exact registered SPIFFE identity; no wildcard. | None |
342
343
  | `destinations.<name>.predicates`, `.valid_for` | Same value/duration contract as classes. | Empty predicates; duration required |
343
344
  | `destinations.<name>.timeout_ms` | Integer 1–2147483647 without A2A; 1–60000 for every destination when A2A is enabled, matching its generated deadline. | `10000` |
344
- | `a2a.public_base_url`, `a2a.local_handler_url` | Optional A2A block; both required if present. Public URL is HTTPS with no path except optional `/`; the HTTP(S) local handler path must be exactly `/a2a/v1`, with no escaped spelling. Neither URL permits query or fragment markers, even empty ones. Managed A2A state paths and a 60-second deadline are generated. No A2A self-route is added. | A2A disabled |
345
+ | `a2a.public_base_url`, `a2a.local_handler_url` | Optional A2A block; both required if present. Public URL is HTTPS with no path except optional `/`; the HTTP(S) local handler path must be exactly `/a2a/v1`, with no escaped spelling. Neither URL permits query or fragment markers, even empty ones. Managed A2A state paths, a 60-second deadline and retry-state capacity are generated; the capacity holds the acknowledgement of the largest reply, which carries the peer's reply to your agent. No A2A self-route is added. | A2A disabled |
345
346
 
346
347
  URL hosts are ASCII DNS names or IP addresses; URL fields reject whitespace,
347
348
  backslashes, embedded credentials and fragments. Basic replay history
@@ -438,7 +439,7 @@ The laptop case. The CLI creates a certificate authority on this machine and sig
438
439
 
439
440
  **Renewal.** `aac agent renew --agent <name>` issues fresh certificates from the same CA.
440
441
 
441
- #### I bring my own CA
442
+ #### I bring my own CA certificate
442
443
 
443
444
  Your own issuer has signed the agent's certificates, and your CA private key never reaches this machine. Certificate source alone does not qualify a production deployment.
444
445
 
@@ -507,7 +508,7 @@ name and `<tenant-id>` the tenant id AAC allocated.
507
508
  For what each of these proves, who else ever gets a copy, what never leaves you
508
509
  and how the pieces fit together — including when your own certificate authority
509
510
  signs instead of this tool — see
510
- [Keys and certificates](https://cascadeauth.github.io/aac-starter-guide/keys-and-certificates.html).
511
+ [Keys and certificates](https://docs.cascadeauth.com/overview/keys-and-certificates/).
511
512
 
512
513
  <!-- inventory-table:start -->
513
514
  | No. | Material | Where | Used for | Lifetime | Back up? |
@@ -740,6 +741,7 @@ aac profile show stage --output table # stored and effective values,
740
741
  aac profile create prod --admin-url https://aac-admin.example.com \
741
742
  --data-plane-url https://aac-data.example.com
742
743
  aac profile update main # edit the built-in baseline
744
+ aac profile update stage --unbind # clear the local tenant association
743
745
  aac profile delete prod # local only; never touches the server
744
746
  ```
745
747
 
@@ -756,6 +758,41 @@ stored value.
756
758
  for it; only `aac tenant register`, `aac init` and `aac sso login` write it.
757
759
  It is your organisation's identifier in the federation, allocated by AAC at
758
760
  registration in the form `tnt-<uuid>`, and it never changes.
761
+ A binding makes that tenant the profile's stored default; it does not grant
762
+ membership or configure an identity provider. Successful sign-in to an existing
763
+ tenant binds the profile; registration binds a newly created tenant.
764
+
765
+ A stale tenant binding affects only that profile. `aac profile list` and
766
+ `aac profile show` mark invalid bindings without printing the invalid values;
767
+ other profiles remain usable. Operational commands reject a selected invalid
768
+ profile with exit 3, even if a tenant flag or environment override is supplied.
769
+ File syntax, profile-name and size limits still apply to the entire file.
770
+
771
+ Use `aac profile update NAME --unbind` to remove the local `tenant_id` and its
772
+ saved hosted-domain metadata. Endpoints, other settings, credentials and the
773
+ server-side tenant are preserved. The flag works without prompts, may accompany
774
+ endpoint flags, and also works on `main` or an already-unbound profile. It
775
+ refuses profiles with unresolved registration or API-key rotation state;
776
+ finish that recovery first. Then bind the profile through registration or
777
+ successful sign-in. Endpoint-only updates and confirmed profile deletion also
778
+ work on stale profiles; reads never rewrite the config file.
779
+
780
+ For an existing tenant, the repair sequence is:
781
+
782
+ ```bash
783
+ aac profile update stage --unbind
784
+ # Bind profile stage to the correct existing tenant:
785
+ aac sso login --profile stage --tenant-id '<TENANT_ID>'
786
+ ```
787
+
788
+ **`aac sso login --profile stage --tenant-id '<TENANT_ID>'` binds profile
789
+ `stage` to that tenant.** Although the verb is `login`, successful authentication
790
+ also saves the tenant association; there is no separate `--bind` flag. Replace
791
+ `<TENANT_ID>` with the intended tenant's canonical ID. An endpoint correction
792
+ or expired session normally does not need
793
+ unbinding. Unbinding is local and does not log out, revoke access, or change
794
+ existing agent records. See the [profile repair guide](https://docs.cascadeauth.com/cli/guide/#repair-a-stale-profile-binding)
795
+ for the exact config fields removed and recovery constraints.
759
796
 
760
797
  `AAC_CLI_HOME` relocates the whole `~/.aac` tree. Use it for a throwaway
761
798
  test home, or to keep separate accounts hard-walled from each other:
@@ -773,13 +810,39 @@ Two credential files can coexist under `~/.aac/credentials/`:
773
810
  (`aac chain`, `aac trust-anchor`) present it. Only a hash of it exists
774
811
  on the server, so a lost key is replaced, never recovered.
775
812
  * `<tenant-id>.session` holds the short-lived session that `aac sso login`
776
- creates. The administration commands (`aac tenant`, `aac sso`) use it
777
- automatically. Sessions cannot be refreshed: when one expires, any
778
- administration command tells you to sign in again, and there is no
779
- refresh token that could be stolen.
813
+ creates. Authenticated administration commands use it automatically;
814
+ public reads such as `aac sso describe` do not use credentials.
815
+ Sessions have no refresh token. You can sign in again before expiry to
816
+ replace the cached session; after expiry, commands that need it ask you
817
+ to sign in again.
780
818
 
781
819
  ## Signing in
782
820
 
821
+ Inspect the available sign-in connections before starting a login:
822
+
823
+ ```bash
824
+ aac sso describe --profile stage # shared/platform candidates
825
+ aac sso describe --profile stage --tenant-id '<TENANT_ID>' # tenant plus shared candidates
826
+ ```
827
+
828
+ `sso describe` reads public metadata from the profile's admin endpoint and
829
+ prints the complete descriptor as JSON on stdout. No credentials or prior
830
+ sign-in are required, and it does not bind a profile or change cached credentials.
831
+ The output includes `connections`, `count`, and, for the tenant route,
832
+ `tenant_id`; connection fields describe issuers, client IDs and available flow
833
+ endpoints. An empty connection list is a successful read.
834
+
835
+ Only an explicit `--tenant-id` selects the tenant descriptor. Omitting it
836
+ always reads shared/platform candidates, even when the profile or the
837
+ `AAC_TENANT_ID` environment variable supplies a tenant. Replace `<TENANT_ID>`
838
+ with the intended tenant's canonical ID. The CLI still checks your local
839
+ configuration and selected profile. If it reports an invalid stored tenant
840
+ binding, clear it with `aac profile update stage --unbind`, then retry.
841
+ Output is JSON only (`--output json` is optional); errors go to stderr.
842
+ See the [command reference](https://docs.cascadeauth.com/cli/reference/aac-sso-describe/).
843
+
844
+ Then sign in using the connection you need:
845
+
783
846
  ```bash
784
847
  aac sso login --profile stage --idp github # developer tenant: GitHub or Google
785
848
  aac sso login --profile prod \
@@ -797,6 +860,15 @@ enterprise tenant signs in through its Microsoft Entra ID connection, named
797
860
  by the connection's exact URL with `--idp-url`; when several connections
798
861
  are registered, the error message lists the exact commands to choose one.
799
862
 
863
+ Sessions last **4 hours by default**. Your administrator may configure a
864
+ different lifetime for the deployment or connection. You can run
865
+ `aac sso login` again before expiry: successful authentication replaces the
866
+ cached session with a new one whose lifetime starts at that login. For example,
867
+ with the four-hour default, signing in at 09:00 gives an expiry of 13:00;
868
+ signing in successfully again at 12:00 gives the new session an expiry of 16:00.
869
+ Use `aac sso whoami --profile NAME --output table` to check the current
870
+ expiration. If the session expires, sign in again; no refresh token is issued.
871
+
800
872
  Sign-in uses the device flow by default, so the browser can be on another
801
873
  machine and headless hosts work. Google connections use a loopback flow
802
874
  instead, because Google's device flow is scope-restricted; `--flow`
@@ -813,11 +885,16 @@ server-returned domain. Existing agents expose read-only identities through
813
885
  or `--field workload-spiffe-id`. A successful scalar is one unquoted line;
814
886
  an unavailable value or unhealthy agent fails with empty stdout. `--field`
815
887
  cannot be combined with explicit `--output`, or with agent `--remote`.
888
+ Scalar profile inspection rejects an invalid stored binding. Structured
889
+ inspection remains available and reports invalid effective IDs as `null` with
890
+ an `error`; invalid list bindings carry `binding_error`, and show bindings carry
891
+ `binding.error`. Their invalid stored tenant IDs are redacted as `null`.
816
892
 
817
893
  Every command that reports a result prints one JSON document to standard
818
894
  output by default, with progress notes and diagnostics on standard error,
819
- so the JSON stays parseable in scripts; `--output table` prints a readable
820
- table instead. The three profile writers are the exception: `aac profile
895
+ so the JSON stays parseable in scripts; where supported, `--output table`
896
+ prints a readable table instead. `aac sso describe` supports JSON only.
897
+ The three profile writers are the exception: `aac profile
821
898
  create`, `update` and `delete` confirm on standard error, leave standard
822
899
  output empty and take no `--output`.
823
900
 
@@ -828,6 +905,18 @@ output empty and take no `--output`.
828
905
  | `2` | Usage error: an invalid flag, value or flag combination. |
829
906
  | `3` | A local configuration or state problem: profile, credential file, cached session or agent. |
830
907
  | `4` | Transport failure: an endpoint could not be reached. |
908
+ | `130` | Interrupted by the operator (Ctrl-C). |
909
+
910
+ Ctrl-C stops `aac` and CLI-owned `aeg` without a traceback, with one concise
911
+ diagnostic on standard error. A remote change may already have committed;
912
+ follow any recovery guidance and retain pending registration or key-operation
913
+ state. The CLI does not roll back or automatically retry a remote change.
914
+
915
+ Walking away from sign-in retains the existing deadlines: device sign-in waits
916
+ for the identity provider's device-code lifetime (300 seconds if absent), then
917
+ exits `1` with instructions to run login again; a PKCE browser sign-in without
918
+ a callback gives up after 300 seconds with exit `3`. HTTP timeouts remain exit
919
+ `4`. These outcomes are distinct from Ctrl-C; there is no shorter patience cap.
831
920
 
832
921
  If AAC asks you to slow down, the command shows the interval to wait. The
833
922
  CLI never replays a change on its own; wait and rerun deliberately.
@@ -902,7 +991,7 @@ are described by their `--help`.
902
991
  ### Keys
903
992
 
904
993
  Replacing the tenant-admin key is immediate and forward-only: the previous
905
- key stops verifying at once and can never be reinstated. The trust-anchor
994
+ key stops verifying at once and can never be reinstated. The trust anchor
906
995
  publisher signs its uploads with this key and loads it once, at start, so
907
996
  sequence the change: stop the publisher, register the new public half, put
908
997
  the new private key where the publisher's configuration points, start the
@@ -7,12 +7,13 @@ which tenant they belong to and what they were authorised to do. A
7
7
  runs beside an AAC sidecar that signs outgoing requests and verifies
8
8
  incoming ones against public trust material the tenant publishes.
9
9
 
10
- New to AAC? [What AAC is for](https://cascadeauth.github.io/aac-starter-guide/#what-aac-is-for)
10
+ New to AAC? [What AAC is for](https://docs.cascadeauth.com/overview/#what-aac-is-for)
11
11
  explains what delegated authority between agents buys you, and what it does not.
12
12
 
13
- The [CLI user guide and complete command reference](https://cascadeauth.github.io/aac-starter-guide/cli/)
13
+ The [CLI user guide and complete command reference](https://docs.cascadeauth.com/cli/)
14
14
  cover developer and enterprise onboarding, everyday administration and recovery.
15
- Select documentation matching `aac --version`.
15
+ These pages describe the latest supported release. Upgrade the CLI to use the
16
+ documented commands, and check your installation with `aac --version`.
16
17
 
17
18
  The `aac` command is how a tenant's operator does everything that is not
18
19
  done by the sidecar itself: register the tenant, sign in, manage keys,
@@ -50,7 +51,7 @@ Python 3.11 to 3.14 is supported. Release checks test all four versions.
50
51
  ## Use `aeg` to view agent execution graphs
51
52
 
52
53
  Installing or upgrading `aac-cli` supplies both `aac` and `aeg`.
53
- Use the [AEG guide and command reference](https://cascadeauth.github.io/aac-starter-guide/aeg.html)
54
+ Use the [AEG guide and command reference](https://docs.cascadeauth.com/aeg/)
54
55
  for listing local and central observations, rendering evidence, and selecting a profile.
55
56
 
56
57
  `aeg list --profile PROFILE --since 7d` discovers participant-visible central
@@ -84,7 +85,7 @@ Google developer sign-in. See “Setting up aac-cli on a new computer” below
84
85
  for the existing-tenant sign-in and material preparation steps.
85
86
 
86
87
  Setup uses the tenant's assigned trust domain and writes the files the
87
- trust-anchor publisher and Docker Compose need, as well as the sidecar
88
+ trust anchor publisher and Docker Compose need, as well as the sidecar
88
89
  configuration. By default it generates development keys and certificates;
89
90
  the certificate choices below explain how to supply your own instead.
90
91
 
@@ -314,7 +315,7 @@ and destination maps allow at most 128 entries each.
314
315
  | `destinations.<name>.audience_pattern` | Required exact registered SPIFFE identity; no wildcard. | None |
315
316
  | `destinations.<name>.predicates`, `.valid_for` | Same value/duration contract as classes. | Empty predicates; duration required |
316
317
  | `destinations.<name>.timeout_ms` | Integer 1–2147483647 without A2A; 1–60000 for every destination when A2A is enabled, matching its generated deadline. | `10000` |
317
- | `a2a.public_base_url`, `a2a.local_handler_url` | Optional A2A block; both required if present. Public URL is HTTPS with no path except optional `/`; the HTTP(S) local handler path must be exactly `/a2a/v1`, with no escaped spelling. Neither URL permits query or fragment markers, even empty ones. Managed A2A state paths and a 60-second deadline are generated. No A2A self-route is added. | A2A disabled |
318
+ | `a2a.public_base_url`, `a2a.local_handler_url` | Optional A2A block; both required if present. Public URL is HTTPS with no path except optional `/`; the HTTP(S) local handler path must be exactly `/a2a/v1`, with no escaped spelling. Neither URL permits query or fragment markers, even empty ones. Managed A2A state paths, a 60-second deadline and retry-state capacity are generated; the capacity holds the acknowledgement of the largest reply, which carries the peer's reply to your agent. No A2A self-route is added. | A2A disabled |
318
319
 
319
320
  URL hosts are ASCII DNS names or IP addresses; URL fields reject whitespace,
320
321
  backslashes, embedded credentials and fragments. Basic replay history
@@ -411,7 +412,7 @@ The laptop case. The CLI creates a certificate authority on this machine and sig
411
412
 
412
413
  **Renewal.** `aac agent renew --agent <name>` issues fresh certificates from the same CA.
413
414
 
414
- #### I bring my own CA
415
+ #### I bring my own CA certificate
415
416
 
416
417
  Your own issuer has signed the agent's certificates, and your CA private key never reaches this machine. Certificate source alone does not qualify a production deployment.
417
418
 
@@ -480,7 +481,7 @@ name and `<tenant-id>` the tenant id AAC allocated.
480
481
  For what each of these proves, who else ever gets a copy, what never leaves you
481
482
  and how the pieces fit together — including when your own certificate authority
482
483
  signs instead of this tool — see
483
- [Keys and certificates](https://cascadeauth.github.io/aac-starter-guide/keys-and-certificates.html).
484
+ [Keys and certificates](https://docs.cascadeauth.com/overview/keys-and-certificates/).
484
485
 
485
486
  <!-- inventory-table:start -->
486
487
  | No. | Material | Where | Used for | Lifetime | Back up? |
@@ -713,6 +714,7 @@ aac profile show stage --output table # stored and effective values,
713
714
  aac profile create prod --admin-url https://aac-admin.example.com \
714
715
  --data-plane-url https://aac-data.example.com
715
716
  aac profile update main # edit the built-in baseline
717
+ aac profile update stage --unbind # clear the local tenant association
716
718
  aac profile delete prod # local only; never touches the server
717
719
  ```
718
720
 
@@ -729,6 +731,41 @@ stored value.
729
731
  for it; only `aac tenant register`, `aac init` and `aac sso login` write it.
730
732
  It is your organisation's identifier in the federation, allocated by AAC at
731
733
  registration in the form `tnt-<uuid>`, and it never changes.
734
+ A binding makes that tenant the profile's stored default; it does not grant
735
+ membership or configure an identity provider. Successful sign-in to an existing
736
+ tenant binds the profile; registration binds a newly created tenant.
737
+
738
+ A stale tenant binding affects only that profile. `aac profile list` and
739
+ `aac profile show` mark invalid bindings without printing the invalid values;
740
+ other profiles remain usable. Operational commands reject a selected invalid
741
+ profile with exit 3, even if a tenant flag or environment override is supplied.
742
+ File syntax, profile-name and size limits still apply to the entire file.
743
+
744
+ Use `aac profile update NAME --unbind` to remove the local `tenant_id` and its
745
+ saved hosted-domain metadata. Endpoints, other settings, credentials and the
746
+ server-side tenant are preserved. The flag works without prompts, may accompany
747
+ endpoint flags, and also works on `main` or an already-unbound profile. It
748
+ refuses profiles with unresolved registration or API-key rotation state;
749
+ finish that recovery first. Then bind the profile through registration or
750
+ successful sign-in. Endpoint-only updates and confirmed profile deletion also
751
+ work on stale profiles; reads never rewrite the config file.
752
+
753
+ For an existing tenant, the repair sequence is:
754
+
755
+ ```bash
756
+ aac profile update stage --unbind
757
+ # Bind profile stage to the correct existing tenant:
758
+ aac sso login --profile stage --tenant-id '<TENANT_ID>'
759
+ ```
760
+
761
+ **`aac sso login --profile stage --tenant-id '<TENANT_ID>'` binds profile
762
+ `stage` to that tenant.** Although the verb is `login`, successful authentication
763
+ also saves the tenant association; there is no separate `--bind` flag. Replace
764
+ `<TENANT_ID>` with the intended tenant's canonical ID. An endpoint correction
765
+ or expired session normally does not need
766
+ unbinding. Unbinding is local and does not log out, revoke access, or change
767
+ existing agent records. See the [profile repair guide](https://docs.cascadeauth.com/cli/guide/#repair-a-stale-profile-binding)
768
+ for the exact config fields removed and recovery constraints.
732
769
 
733
770
  `AAC_CLI_HOME` relocates the whole `~/.aac` tree. Use it for a throwaway
734
771
  test home, or to keep separate accounts hard-walled from each other:
@@ -746,13 +783,39 @@ Two credential files can coexist under `~/.aac/credentials/`:
746
783
  (`aac chain`, `aac trust-anchor`) present it. Only a hash of it exists
747
784
  on the server, so a lost key is replaced, never recovered.
748
785
  * `<tenant-id>.session` holds the short-lived session that `aac sso login`
749
- creates. The administration commands (`aac tenant`, `aac sso`) use it
750
- automatically. Sessions cannot be refreshed: when one expires, any
751
- administration command tells you to sign in again, and there is no
752
- refresh token that could be stolen.
786
+ creates. Authenticated administration commands use it automatically;
787
+ public reads such as `aac sso describe` do not use credentials.
788
+ Sessions have no refresh token. You can sign in again before expiry to
789
+ replace the cached session; after expiry, commands that need it ask you
790
+ to sign in again.
753
791
 
754
792
  ## Signing in
755
793
 
794
+ Inspect the available sign-in connections before starting a login:
795
+
796
+ ```bash
797
+ aac sso describe --profile stage # shared/platform candidates
798
+ aac sso describe --profile stage --tenant-id '<TENANT_ID>' # tenant plus shared candidates
799
+ ```
800
+
801
+ `sso describe` reads public metadata from the profile's admin endpoint and
802
+ prints the complete descriptor as JSON on stdout. No credentials or prior
803
+ sign-in are required, and it does not bind a profile or change cached credentials.
804
+ The output includes `connections`, `count`, and, for the tenant route,
805
+ `tenant_id`; connection fields describe issuers, client IDs and available flow
806
+ endpoints. An empty connection list is a successful read.
807
+
808
+ Only an explicit `--tenant-id` selects the tenant descriptor. Omitting it
809
+ always reads shared/platform candidates, even when the profile or the
810
+ `AAC_TENANT_ID` environment variable supplies a tenant. Replace `<TENANT_ID>`
811
+ with the intended tenant's canonical ID. The CLI still checks your local
812
+ configuration and selected profile. If it reports an invalid stored tenant
813
+ binding, clear it with `aac profile update stage --unbind`, then retry.
814
+ Output is JSON only (`--output json` is optional); errors go to stderr.
815
+ See the [command reference](https://docs.cascadeauth.com/cli/reference/aac-sso-describe/).
816
+
817
+ Then sign in using the connection you need:
818
+
756
819
  ```bash
757
820
  aac sso login --profile stage --idp github # developer tenant: GitHub or Google
758
821
  aac sso login --profile prod \
@@ -770,6 +833,15 @@ enterprise tenant signs in through its Microsoft Entra ID connection, named
770
833
  by the connection's exact URL with `--idp-url`; when several connections
771
834
  are registered, the error message lists the exact commands to choose one.
772
835
 
836
+ Sessions last **4 hours by default**. Your administrator may configure a
837
+ different lifetime for the deployment or connection. You can run
838
+ `aac sso login` again before expiry: successful authentication replaces the
839
+ cached session with a new one whose lifetime starts at that login. For example,
840
+ with the four-hour default, signing in at 09:00 gives an expiry of 13:00;
841
+ signing in successfully again at 12:00 gives the new session an expiry of 16:00.
842
+ Use `aac sso whoami --profile NAME --output table` to check the current
843
+ expiration. If the session expires, sign in again; no refresh token is issued.
844
+
773
845
  Sign-in uses the device flow by default, so the browser can be on another
774
846
  machine and headless hosts work. Google connections use a loopback flow
775
847
  instead, because Google's device flow is scope-restricted; `--flow`
@@ -786,11 +858,16 @@ server-returned domain. Existing agents expose read-only identities through
786
858
  or `--field workload-spiffe-id`. A successful scalar is one unquoted line;
787
859
  an unavailable value or unhealthy agent fails with empty stdout. `--field`
788
860
  cannot be combined with explicit `--output`, or with agent `--remote`.
861
+ Scalar profile inspection rejects an invalid stored binding. Structured
862
+ inspection remains available and reports invalid effective IDs as `null` with
863
+ an `error`; invalid list bindings carry `binding_error`, and show bindings carry
864
+ `binding.error`. Their invalid stored tenant IDs are redacted as `null`.
789
865
 
790
866
  Every command that reports a result prints one JSON document to standard
791
867
  output by default, with progress notes and diagnostics on standard error,
792
- so the JSON stays parseable in scripts; `--output table` prints a readable
793
- table instead. The three profile writers are the exception: `aac profile
868
+ so the JSON stays parseable in scripts; where supported, `--output table`
869
+ prints a readable table instead. `aac sso describe` supports JSON only.
870
+ The three profile writers are the exception: `aac profile
794
871
  create`, `update` and `delete` confirm on standard error, leave standard
795
872
  output empty and take no `--output`.
796
873
 
@@ -801,6 +878,18 @@ output empty and take no `--output`.
801
878
  | `2` | Usage error: an invalid flag, value or flag combination. |
802
879
  | `3` | A local configuration or state problem: profile, credential file, cached session or agent. |
803
880
  | `4` | Transport failure: an endpoint could not be reached. |
881
+ | `130` | Interrupted by the operator (Ctrl-C). |
882
+
883
+ Ctrl-C stops `aac` and CLI-owned `aeg` without a traceback, with one concise
884
+ diagnostic on standard error. A remote change may already have committed;
885
+ follow any recovery guidance and retain pending registration or key-operation
886
+ state. The CLI does not roll back or automatically retry a remote change.
887
+
888
+ Walking away from sign-in retains the existing deadlines: device sign-in waits
889
+ for the identity provider's device-code lifetime (300 seconds if absent), then
890
+ exits `1` with instructions to run login again; a PKCE browser sign-in without
891
+ a callback gives up after 300 seconds with exit `3`. HTTP timeouts remain exit
892
+ `4`. These outcomes are distinct from Ctrl-C; there is no shorter patience cap.
804
893
 
805
894
  If AAC asks you to slow down, the command shows the interval to wait. The
806
895
  CLI never replays a change on its own; wait and rerun deliberately.
@@ -875,7 +964,7 @@ are described by their `--help`.
875
964
  ### Keys
876
965
 
877
966
  Replacing the tenant-admin key is immediate and forward-only: the previous
878
- key stops verifying at once and can never be reinstated. The trust-anchor
967
+ key stops verifying at once and can never be reinstated. The trust anchor
879
968
  publisher signs its uploads with this key and loads it once, at start, so
880
969
  sequence the change: stop the publisher, register the new public half, put
881
970
  the new private key where the publisher's configuration points, start the
@@ -10,4 +10,4 @@ entrypoint (`aac_cli.cli:entrypoint`).
10
10
  # test in bin/tests/test_cli_version_parity.py. A literal keeps the
11
11
  # shipped code free of import-time metadata lookups (and their
12
12
  # not-installed failure mode).
13
- __version__ = "0.2.8"
13
+ __version__ = "0.2.10"
@@ -0,0 +1,13 @@
1
+ """`python -m aac_cli` — same entrypoint as the `aac` console script."""
2
+
3
+ import sys
4
+
5
+ try:
6
+ from aac_cli.cli import entrypoint
7
+
8
+ if __name__ == "__main__":
9
+ entrypoint()
10
+ except KeyboardInterrupt:
11
+ # The shared command boundary is not loaded during early imports.
12
+ sys.stderr.write("aac: interrupted\n")
13
+ raise SystemExit(130) from None
@@ -7,6 +7,7 @@ import sys
7
7
  from importlib.resources import files
8
8
 
9
9
  from . import __version__
10
+ from .interrupts import run_command
10
11
 
11
12
 
12
13
  def version_text() -> str:
@@ -15,6 +16,10 @@ def version_text() -> str:
15
16
 
16
17
 
17
18
  def main(argv: list[str] | None = None) -> int:
19
+ return run_command(lambda: _main(argv), prog="aeg")
20
+
21
+
22
+ def _main(argv: list[str] | None = None) -> int:
18
23
  argv = list(sys.argv[1:] if argv is None else argv)
19
24
  if argv == ["--version"]:
20
25
  print(version_text())
@@ -10,7 +10,7 @@ tenants:
10
10
  public trust endpoints.
11
11
  * ``renew`` — in "The CLI creates a development CA" mode, reissues the one-day
12
12
  workload, receipt and HTTPS certificates with fresh keys, and the CA only
13
- when it is expired or inside its renewal window. In "I bring my own CA" mode
13
+ when it is expired or inside its renewal window. With supplied certificates
14
14
  it cannot reissue what it did not sign: it takes replacements from your
15
15
  issuer through the same flags `aac init` accepts. Retired material is
16
16
  archived, never deleted.
@@ -64,7 +64,7 @@ from aac_cli.init_cli import (
64
64
  check_settings_can_be_written,
65
65
  write_settings_files,
66
66
  )
67
- from aac_cli.material_cases import MATERIAL_SUPPLIED, case_for
67
+ from aac_cli.material_cases import MATERIAL_ISSUED_HERE, MATERIAL_SUPPLIED, case_for
68
68
  from aac_cli.secure_files import ensure_managed_directory, managed_path_problem, write_private_file
69
69
  from aac_cli.supplied_material import resolve_supplied_replacement
70
70
 
@@ -619,13 +619,21 @@ def cmd_agent_list(args: argparse.Namespace) -> int:
619
619
  row["certificates"] = case_for(record.material_source).name
620
620
  tenant = tenant_paths(record.tenant_id) if record.tenant_id else None
621
621
  health = assess(agent, tenant, record)
622
+ row["profile_binding_consistent"] = health.profile_binding_consistent
622
623
  row["next_command"] = next_step(agent, tenant, record, health)
623
624
  rows.append(row)
624
625
  if args.output == "table":
625
626
  if not rows:
626
627
  print(f"no agents under {directory}")
627
628
  for row in rows:
628
- print(f"{row['agent']:<16}: {row.get('certificates', row.get('state', '-'))}")
629
+ binding_note = (
630
+ " (profile binding inconsistent)"
631
+ if row.get("profile_binding_consistent") is False
632
+ else ""
633
+ )
634
+ print(
635
+ f"{row['agent']:<16}: {row.get('certificates', row.get('state', '-'))}{binding_note}"
636
+ )
629
637
  return 0
630
638
  print(json.dumps({"agents": rows}, indent=2))
631
639
  return 0
@@ -692,10 +700,10 @@ def add_agent_parser(nouns: argparse._SubParsersAction) -> None:
692
700
  "renew",
693
701
  help="Replace the short-lived certificates; archive the old material.",
694
702
  description=(
695
- 'In "The CLI creates a development CA" mode, reissues the workload, receipt and '
703
+ f'In "{case_for(MATERIAL_ISSUED_HERE).name}" mode, reissues the workload, receipt and '
696
704
  "HTTPS certificates with new keys; the development CA is reissued only when "
697
705
  "expired or within one day of expiry, or with --ca, and a new CA needs the "
698
- 'publisher to publish the new anchor. In "I bring my own CA" mode nothing is '
706
+ f'publisher to publish the new anchor. In "{case_for(MATERIAL_SUPPLIED).name}" mode nothing is '
699
707
  "reissued: pass the replacement certificates your issuer produced, each with its "
700
708
  "key file, and --ca-cert-file if your CA certificate changed too."
701
709
  ),
@@ -708,7 +716,12 @@ def add_agent_parser(nouns: argparse._SubParsersAction) -> None:
708
716
  listing = verbs.add_parser(
709
717
  "list",
710
718
  help="List every agent under the CLI home with its tenant, case and state.",
711
- description="List every agent; an unreadable or unsupported record is reported in that agent's state without hiding the other agents.",
719
+ description=(
720
+ "List every agent; an unreadable or unsupported record is reported in that "
721
+ "agent's state without hiding the other agents. Invalid profile bindings "
722
+ "are reported per agent: JSON includes profile_binding_consistent and the "
723
+ "repair next_command; table output marks inconsistent bindings."
724
+ ),
712
725
  )
713
726
  listing.add_argument(
714
727
  "--output", choices=("json", "table"), default="json", help="Output mode (JSON by default)."
@@ -22,7 +22,13 @@ from aac_cli.agent_layout import (
22
22
  material_inventory,
23
23
  )
24
24
  from aac_cli.agent_record import STEP_MATERIAL, AgentRecord
25
- from aac_cli.config import CliConfigError, cli_home, credentials_path, read_profile_section
25
+ from aac_cli.config import (
26
+ CliConfigError,
27
+ cli_home,
28
+ credentials_path,
29
+ profile_binding_error,
30
+ read_profile_section,
31
+ )
26
32
  from aac_cli.material_cases import MATERIAL_ISSUED_HERE, MATERIAL_SUPPLIED, ca_report_label
27
33
  from aac_cli.secure_files import managed_path_problem
28
34
 
@@ -40,6 +46,7 @@ class CertificateReport:
40
46
  class AgentHealth:
41
47
  setup_complete: bool
42
48
  profile_binding_consistent: bool
49
+ invalid_profile_binding: bool = False
43
50
  missing_files: list[str] = field(default_factory=list)
44
51
  permission_problems: list[str] = field(default_factory=list)
45
52
  certificates: dict[str, CertificateReport] = field(default_factory=dict)
@@ -74,7 +81,9 @@ class AgentHealth:
74
81
  reasons: list[str] = []
75
82
  if not self.setup_complete:
76
83
  reasons.append("setup is incomplete")
77
- if not self.profile_binding_consistent:
84
+ if self.invalid_profile_binding:
85
+ reasons.append("the profile has an invalid stored tenant binding")
86
+ elif not self.profile_binding_consistent:
78
87
  reasons.append("the profile is bound to a different tenant than this agent")
79
88
  reasons.extend(f"missing: {name}" for name in self.missing_files)
80
89
  reasons.extend(self.permission_problems)
@@ -111,10 +120,16 @@ def _certificate_reports(
111
120
 
112
121
  def assess(agent: AgentPaths, tenant: TenantPaths | None, record: AgentRecord) -> AgentHealth:
113
122
  """The offline health assessment both `status` and a completed `init` report."""
114
- profile_tenant = read_profile_section(record.profile).get("tenant_id")
123
+ # Offline inventory diagnoses this agent's profile without allowing one
124
+ # stale association to abort every other agent's inventory. Operational
125
+ # profile resolution stays strict; material paths use the validated record.
126
+ stored = read_profile_section(record.profile, validate_binding=False)
127
+ invalid_binding = profile_binding_error(stored) is not None
115
128
  health = AgentHealth(
116
129
  setup_complete=record.next_step() is None,
117
- profile_binding_consistent=profile_tenant == record.tenant_id,
130
+ profile_binding_consistent=not invalid_binding
131
+ and stored.get("tenant_id") == record.tenant_id,
132
+ invalid_profile_binding=invalid_binding,
118
133
  )
119
134
  if tenant is None:
120
135
  return health
@@ -387,6 +402,8 @@ def next_step(
387
402
  ) -> str | None:
388
403
  """The one next step `status` reports and a refused `init` names; None when there is none."""
389
404
  agent_flag = f"--agent {_quote(agent.name)}"
405
+ if health.invalid_profile_binding:
406
+ return f"aac profile update {_quote(record.profile)} --unbind"
390
407
  if not health.setup_complete:
391
408
  return f"aac init --profile {_quote(record.profile)} {agent_flag}"
392
409
  if tenant is not None: