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.
- {aac_cli-0.2.8/aac_cli.egg-info → aac_cli-0.2.10}/PKG-INFO +106 -17
- {aac_cli-0.2.8 → aac_cli-0.2.10}/README.md +104 -15
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/__init__.py +1 -1
- aac_cli-0.2.10/aac_cli/__main__.py +13 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/aeg_cli.py +5 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/agent_cli.py +19 -6
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/agent_health.py +21 -4
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/cli.py +230 -54
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/config.py +145 -35
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/config_render.py +20 -4
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/init_cli.py +3 -3
- aac_cli-0.2.10/aac_cli/interrupts.py +48 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/material_cases.py +1 -1
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/profiles.py +114 -59
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/reference.py +13 -4
- {aac_cli-0.2.8 → aac_cli-0.2.10/aac_cli.egg-info}/PKG-INFO +106 -17
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli.egg-info/SOURCES.txt +1 -0
- aac_cli-0.2.10/launchers/aac +36 -0
- aac_cli-0.2.10/launchers/aeg +33 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/pyproject.toml +3 -3
- aac_cli-0.2.8/aac_cli/__main__.py +0 -6
- aac_cli-0.2.8/launchers/aac +0 -32
- aac_cli-0.2.8/launchers/aeg +0 -29
- {aac_cli-0.2.8 → aac_cli-0.2.10}/LICENSE +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/MANIFEST.in +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/NOTICE +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/THIRD_PARTY_NOTICES.md +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/__init__.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/cli.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/data/action-taken-v1.json +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/data/predicates-v1.json +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/data/renderer-source.json +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/from_sidecar.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/inputs.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/model.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/narrative.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/provenance.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/schemas.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/temporal.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/theme.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/timeline_source.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/_aeg/visualizer.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/admin_key_pem.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/aeg_list.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/agent_config.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/agent_layout.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/agent_record.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/api_key_rotation_state.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/chain_list.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/dev_material.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/idp_recovery.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/registration_state.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/secure_files.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/sso_login.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/supplied_material.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli/trace_query.py +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli.egg-info/dependency_links.txt +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli.egg-info/requires.txt +0 -0
- {aac_cli-0.2.8 → aac_cli-0.2.10}/aac_cli.egg-info/top_level.txt +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
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`
|
|
820
|
-
table instead.
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
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`
|
|
793
|
-
table instead.
|
|
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
|
|
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
|
|
@@ -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.
|
|
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
|
-
|
|
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 "
|
|
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 "
|
|
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=
|
|
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
|
|
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
|
|
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
|
-
|
|
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=
|
|
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:
|