@knowttl/mg-axi 0.1.4 → 0.1.6

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.
package/README.md CHANGED
@@ -1,1331 +1,366 @@
1
- # mg-axi
2
-
3
- Agent-facing Microsoft Graph CLI with one shared core and domain packs, read-only by default.
4
- The Entra pack comes first, with phased full coverage and gated named writes.
5
-
6
- CLI-01 provides a local TypeScript/AXI shell, strict command catalogue, leaf help and fast version probes.
7
- AUTH-01 adds versioned dedicated-app delegated profiles and explicit login.
8
- AUTH-02 adds certificate and workload-federated application profiles and a client-credentials service.
9
- CORE-01 adds the shared policy-enforced Graph read session, exercised through an injected fixture HTTP transport.
10
- CORE-02 adds session collections, query validation, bounded retries and cancellation under the [read execution contract](docs/execution.md#read-mechanics-and-source-contracts).
11
- API-01 executes `mg-axi api get <path>`, serving the reviewed v1.0 raw surface through that session.
12
- READ-01 executes Entra user list/show through that session in both delegated and application modes; current usage follows below.
13
- READ-02 adds group list/show and direct or transitive member and parent-membership reads through the same session.
14
- READ-04 adds targeted per-user authentication-method reads and the tenant registration report through the same session, with phone numbers redacted.
15
- READ-09 adds directory-role list/show, directory-role-template list/show, directory-role member and scoped-member reads, scoped-role-membership reads, current role-assignment inventory and active/eligible PIM reads through the same session.
16
- READ-03 adds Conditional Access policy, named-location, authentication-strength-policy, combination-configuration, authentication-method-mode, template, auth-context, deleted-policy and deleted-named-location list/show as separate grammar through the same session; Conditional Access usage follows the device and administrative-unit usage below.
17
- READ-05 executes Entra sign-in, directory-audit and provisioning-log list/show through that session; log usage follows the Conditional Access usage below.
18
- READ-07 adds application and service-principal list/show with credential expiry metadata, owner reads, federated-credential reads, and service-principal app-role-assigned-to reads through the same session.
19
- READ-10 adds directory-device and administrative-unit list/show and unit-member reads through the same session.
20
- READ-08 adds service-principal delegated-grant list and app-role-assignment list/show consent reads for a named client through the same session.
21
- EXT-01 (domains) adds tenant-domain list/show, per-domain verification and service-configuration DNS record reads, and top-level domain DNS record reads through the same session.
22
- EXT-01 (certificate auth) adds PKI configuration and certificate-authority list/show/count reads with default certificate-blob omission through the same session.
23
- EXT-01 (directory subscriptions) adds commercial-subscription list/show/count reads with compact licence rows through the same session.
24
- EXT-01 (on-premises synchronization) adds on-premises directory-synchronization list/show reads with Global Administrator role guidance through the same session.
25
- EXT-01 (terms of use) adds terms-of-use agreement list/show, per-agreement acceptance list/show and tenant-wide agreement-acceptance list/show reads with Security Reader role and Entra ID P1 guidance through the same session.
26
- EXT-01 (directory objects) adds directory-object list/show/count reads with @odata.type subtype rows and Directory.Read.All guidance through the same session.
27
- EXT-01 (deleted items) adds soft-deleted user, group, application, service-principal and administrative-unit list/count reads plus deleted-item show with per-type least-privilege scope guidance through the same session.
28
- EXT-01 (contacts) adds organizational-contact list/show/count reads with minimal personal-data rows and OrgContact.Read.All guidance through the same session.
29
- EXT-01m (contact navigation) adds per-contact manager and direct-report reads (show-manager, list/show/count-direct-reports with user/contact casts) as directory objects with type plus minimal rows through the same session.
30
- EXT-01n (contact membership) adds per-contact memberOf and transitiveMemberOf reads (list/show/count-member-of with transitive and group/administrativeUnit casts) as directory objects with type plus minimal rows through the same session.
31
- EXT-03 (identity providers) adds workforce identity-provider list/show/count/available-types reads with secret scrubbing through the same session.
32
- EXT-01 (federation configurations) adds workforce directory federation-configuration list/show/count/available-types reads with default signing-certificate omission through the same session.
33
- EXT-03 (data policy operations) adds workforce data-policy-operation list/show/count reads with storage-location redaction through the same session.
34
- EXT-03 (risk prevention) adds workforce fraud-protection-provider, web-application-firewall-provider and web-application-firewall-verification list/show/count reads plus verification provider show with key-material scrubbing through the same delegated session.
35
- READ-06 executes Entra risky-user, risk-detection, risky-service-principal (with per-principal history) and service-principal-risk-detection list/show through that session; risk usage follows the log usage below.
36
- EXT-01 (organization) adds tenant-organization list/show, default sign-in branding metadata and locale branding reads through the same session.
37
- EXT-04 (contracts) adds partner-tenant customer-contract list/show/count through the same session.
38
- EXT-04 (delegated-admin) adds partner-tenant delegated-admin customer and relationship list/show through the same session.
39
- EXT-04 (multi-tenant-organization) adds multitenant-organization show, join-request show and member-tenant list/count through the same session.
40
- EXT-04 (tenant-information) adds tenant-information show by domain name or tenant ID through the allowlisted session function-argument contract.
41
- EXT-01 (group lifecycle) adds group lifecycle-policy and group setting-template list/show/count reads through the same session.
42
- EXT-01 (custom security attributes) adds attribute-set, custom-security-attribute-definition and allowed-value list/show/count reads with attribute-role denial guidance through the same session.
43
- Tests use fixture credential and transport providers; no tenant, real credentials or network access are required for help or an unconfigured home view.
44
-
45
- Run commands non-interactively as `npx -y @knowttl/mg-axi ...`: no global install needed and no interactive prompts.
46
- Version pinning is the installer's choice: use `npx -y @knowttl/mg-axi@<version> ...` to select a specific release.
1
+ <h1 align="center">mg-axi</h1>
47
2
 
48
- ```sh
49
- npx -y @knowttl/mg-axi --version
50
- npx -y @knowttl/mg-axi --help
51
- npx -y @knowttl/mg-axi entra user list --help
52
- ```
53
-
54
- For a local build, use the Node requirement and pinned pnpm version declared in [package.json](package.json):
55
-
56
- ```sh
57
- corepack pnpm install --frozen-lockfile --ignore-scripts --config.confirm-modules-purge=false
58
- corepack pnpm build
59
- node dist/bin/mg-axi.js
60
- node dist/bin/mg-axi.js --version
61
- node dist/bin/mg-axi.js entra user list --help
62
- node dist/bin/mg-axi.js entra user show --help
63
- ```
64
-
65
- Run checkout commands from the repository root with `node dist/bin/mg-axi.js`.
66
- To install the built checkout binary on PATH, run this explicitly from the repository root:
67
-
68
- ```sh
69
- npm link --ignore-scripts
70
- mg-axi --version
71
- mg-axi --help
72
- ```
73
-
74
- Without linking, substitute `node dist/bin/mg-axi.js` from the repository root wherever examples or CLI output use `mg-axi`.
75
- The no-args home view and `--help` hints print the `npx -y @knowttl/mg-axi ...` form, which works with or without a linked checkout.
76
-
77
- The home view reports unavailable tenant summaries explicitly.
78
- `entra user list` defaults to basic properties (`id`, `displayName`, `userPrincipalName`, `mail`); `entra user show --id <user-id-or-upn>` defaults to the richer server property set.
79
- `--select` requests properties from the [supported user property set](src/entra-users.ts); `--fields` projects locally and must be a subset of the fetched selection.
80
- Text values longer than 500 characters are truncated, including strings in `businessPhones`; `--full` removes text truncation without lifting redaction or row caps.
81
- Explicit null values stay null, missing properties stay absent, and denied reads return structured errors rather than empty results.
82
- User lists accept `--filter` for an OData filter and default to a 100-row cap; `--limit` changes the cap, while the incompatible `--all` follows pages within request, byte and deadline budgets.
83
- Partial lists report `complete: false` with a uniform `count` line (`1 users shown, more available`), a reason and an opaque `cursor` preserving unreturned rows.
84
- Resume with `--cursor <cursor-from-output>` using the same profile, authentication scopes and API version; original `--select` and `--filter` values may be repeated or omitted, and conflicting values fail validation.
85
- Repeat `--fields` and `--full` when the same local view is wanted; these are not saved in the cursor.
86
- Delegated user reads default to `https://graph.microsoft.com/User.Read.All` with `--scopes` available for least-privilege basics; application profiles use the configured Graph `.default` audience and reject delegated scopes.
87
- All delegated reads, including raw reads and cursor resumes, reject scopes outside `READ_SCOPES` in the [shared session](src/graph-session.ts) before credential acquisition; [Graph coverage](docs/graph-coverage.md) explains the operation-specific read permission choices.
88
- Write scopes are refused with `VALIDATION_ERROR` and a list of supported read scopes.
89
- User reads acquire credentials silently; a resume containing only buffered rows can finish without another Graph request.
90
- WRITE-02 adds a named write: `mg-axi entra user update --user <user-id-or-upn> --account-enabled true|false` sets one user's `accountEnabled` through `PATCH /users/{id}` with only that property sent.
91
- `--account-enabled` accepts exactly `true` or `false`; uppercase and whitespace-padded values are rejected.
92
- The command supports only `--api-version v1.0`; beta writes are rejected before credentials or HTTP.
93
- Without `--execute` the command previews the desired-state diff read through the user show route and journals nothing; an already-desired value is a no-op with exit 0.
94
- Preview also requires the write-enabled profile and operation allowlist described below.
95
- User IDs and UPNs are accepted, including guest UPNs containing `#EXT#`; pass the literal identifier, quoted for the shell, rather than percent-encoding it.
96
- The first lookup pins the Graph object ID for the PATCH and subsequent reads; successful execution and no-op results return that object ID in `user.id`.
97
- Enabling and disabling are disruptive: every `--execute` run needs `--confirm '<user-id-or-upn>'` repeating the target exactly, including already-desired states.
98
- After a successful PATCH the command rereads the user and reports a `WRITE_CONFLICT` when the value is not what was sent; user-update answers 204 with an empty body, so the reread is the only proof.
99
- A failed verification read reports `OUTCOME_UNKNOWN`; read back the pinned object ID before proceeding and never replay the intent.
100
- Fresh reads do not make this write atomic: no ETag condition is sent, so another actor can change the account between the read, PATCH and verification.
101
- The least-privileged permission pair is `User.EnableDisableAccount.All` plus `User.Read.All` in both modes.
102
- Delegated PATCH credentials request both scopes together; reads request `User.Read.All`, and credentials are acquired silently.
103
- Use explicit `mg-axi login --profile soc --scopes https://graph.microsoft.com/User.EnableDisableAccount.All,https://graph.microsoft.com/User.Read.All` to sign in for the write.
104
- Application profiles use the configured Graph `.default` audience with the pair admin-consented on the app registration; the command does not request a per-operation scope subset.
105
- The update command rejects caller-supplied `--scopes`.
106
- Delegated callers need `Privileged Authentication Administrator` for admin targets and must generally outrank the target; app-only callers need the pair plus a higher-privileged admin role assignment, and 403 denials surface both rules because a 403 never says which prerequisite is missing.
107
- WRITE-03 adds a named action write: `mg-axi entra user revoke-sessions --user <user-id>` revokes one user's sign-in sessions through `POST /users/{id}/revokeSignInSessions` with no request body.
108
- The command treats an accepted 2xx status as success without validating the response body or performing a verification reread; it returns `user.id`, `user.sessionsRevoked: true` and `auditId`.
109
- The command supports only `--api-version v1.0`; beta writes are rejected before credentials or HTTP.
110
- Without `--execute` the command previews the action and journals nothing: the preview states that Graph resets `signInSessionsValidFromDateTime`, invalidating issued refresh tokens and browser session cookies so the user must sign in again, and that the action cannot be undone - there is no rollback.
111
- The preview also carries the two Microsoft-stated limits: token revocation can lag a few minutes after the call returns, and external users are unaffected because they sign in through their home tenant.
112
- `--user` takes the user object ID only; UPNs are not resolved.
113
- The target is verified as a user through the user show route before preview and again before sending; a failed or malformed user read blocks the operation.
114
- Revocation is always disruptive: every `--execute` run needs `--confirm '<user-id>'` repeating the target exactly.
115
- The revocation action's least-privileged permission is `User.RevokeSessions.All` in both modes.
116
- Delegated revocation credentials request that scope; reads request `User.ReadBasic.All`, and credentials are acquired silently.
117
- Use explicit `mg-axi login --profile soc --scopes https://graph.microsoft.com/User.RevokeSessions.All,https://graph.microsoft.com/User.ReadBasic.All` to sign in for the write.
118
- Application profiles use the configured Graph `.default` audience with `User.RevokeSessions.All` and the prerequisite [user-read permission](https://learn.microsoft.com/en-us/graph/api/user-get?view=graph-rest-1.0), `User.Read.All`, admin-consented on the app registration; the command does not request a per-operation scope subset.
119
- The revoke command rejects caller-supplied `--scopes`.
120
- A 403 denial surfaces the `User.RevokeSessions.All` requirement without inventing a role verdict; a timeout or 5xx after sending reports `OUTCOME_UNKNOWN`, and neither outcome is ever replayed.
121
- Unknown-outcome guidance includes a user read with `--select id`; that read checks target accessibility, not whether revocation took effect.
122
- Unknown flags, unexpected arguments, missing required values and unsupported combinations exit 2 before credential acquisition or HTTP.
123
- Resource identifiers in named commands and raw paths cannot begin with `$` or contain parentheses, except the three allowlisted function-argument routes (the two tenant-information lookups and the subscription commerce-key lookup), whose parenthesised values arrive only as validated CLI flags and are OData-quoted and encoded by the shared session; OData route segments such as `$count`, `$value` and `$ref`, and other function-style segments such as `delta()`, cannot be used as IDs and fail validation before credentials.
124
- Help and successful views, including partial lists, exit 0; authentication, policy and Graph failures exit 1.
125
- Data and structured errors use TOON on stdout; diagnostics belong on stderr.
126
- Bare `-v`, `-V` and `--version` print only the package version without importing the catalogue.
127
-
128
- Create a delegated profile with your organization-owned public client registration, explicit workforce tenant UUID and explicit commercial cloud:
129
-
130
- ```sh
131
- mg-axi profile create --name soc --tenant <tenant-id> --client <client-id> --cloud commercial
132
- mg-axi profile list
133
- mg-axi profile show --profile soc
134
- mg-axi login --profile soc --scopes https://graph.microsoft.com/User.Read.All,https://graph.microsoft.com/GroupMember.Read.All,https://graph.microsoft.com/Group.Read.All,https://graph.microsoft.com/Application.Read.All
135
- ```
136
-
137
- After login, use:
138
-
139
- ```sh
140
- mg-axi entra user list --profile soc --limit 10
141
- mg-axi entra user list --profile soc --select id,displayName,department --fields id,department
142
- mg-axi entra user show --profile soc --id <user-id-or-upn> --full
143
- mg-axi entra group list --profile soc --limit 10
144
- mg-axi entra group show --profile soc --id <group-id> --scopes https://graph.microsoft.com/Group.Read.All
145
- mg-axi entra group member list --profile soc --group <group-id>
146
- mg-axi entra group member list --profile soc --group <group-id> --transitive
147
- mg-axi entra group member-of list --profile soc --group <group-id> --transitive
148
- mg-axi entra application list --profile soc --limit 10
149
- mg-axi entra application show --profile soc --id <application-object-id>
150
- mg-axi entra application owner list --profile soc --application <application-object-id>
151
- mg-axi entra service-principal list --profile soc --limit 10
152
- mg-axi entra service-principal show --profile soc --id <service-principal-object-id>
153
- mg-axi entra service-principal owner list --profile soc --service-principal <service-principal-object-id>
154
- mg-axi entra application federated-credential list --profile soc --application <application-object-id>
155
- mg-axi entra service-principal federated-credential list --profile soc --service-principal <service-principal-object-id>
156
- ```
157
-
158
- Read granted consent for one client service principal; delegated profiles first need explicit login with the read scopes:
159
-
160
- ```sh
161
- mg-axi login --profile soc --scopes https://graph.microsoft.com/Directory.Read.All,https://graph.microsoft.com/Application.Read.All
162
- mg-axi entra service-principal oauth2-grant list --profile soc --service-principal <service-principal-object-id>
163
- mg-axi entra service-principal app-role-assignment list --profile soc --service-principal <service-principal-object-id> --filter "resourceId eq '<resource-id>'"
164
- mg-axi entra service-principal app-role-assignment show --profile soc --service-principal <service-principal-object-id> --id <assignment-id>
165
- mg-axi entra service-principal app-role-assigned-to list --profile soc --service-principal <service-principal-object-id>
166
- ```
167
-
168
- `entra service-principal oauth2-grant list` defaults to `id`, `consentType`, `principalId`, `resourceId` and `scope`: the delegated scopes granted to the client, with `consentType` AllPrincipals covering every user (and an explicit null `principalId`) versus Principal covering the one user named by `principalId`.
169
- `entra service-principal app-role-assignment list` defaults to `id`, `appRoleId`, `resourceDisplayName` and `resourceId`: the app-only roles granted to the client on each resource API.
170
- `entra service-principal app-role-assignment show --service-principal <service-principal-object-id> --id <assignment-id>` returns one such grant with the full reviewed app-role set.
171
- `entra service-principal app-role-assigned-to list` defaults to `id`, `appRoleId`, `principalDisplayName`, `principalId`, `principalType`, `resourceDisplayName` and `resourceId`: the reverse view, principals assigned to one resource service principal; `app-role-assigned-to show` returns one such record with the full reviewed set.
172
- All these views show actual granted consent records; the application's requested permissions (`requiredResourceAccess`) are declared on the application object and are never shown here, and grant creation, revocation and consent belong to later write slices, never to these reads.
173
- `--select` requests properties from the [reviewed grant property sets](src/entra-grants.ts); `--fields` must be a subset of the fetched selection.
174
- `--filter` passes through as plain `$filter` with no `$count` or `ConsistencyLevel` contract; the named-list caps, uniform `count` totals, cursor resume rules and 500-character text truncation described above also apply.
175
- Resume any of these lists with the same `--service-principal` object ID.
176
- Delegated oauth2-grant reads default to `https://graph.microsoft.com/Directory.Read.All` and app-role reads to `https://graph.microsoft.com/Application.Read.All`, while application profiles use the configured `.default` audience; reads never request a write-consent scope such as `DelegatedPermissionGrant.ReadWrite.All`, `Application.ReadWrite.All` or `Directory.ReadWrite.All`.
177
- Delegated callers additionally need a supported directory role per operation (for example Directory Readers, Global Reader or Application Administrator).
178
- Denied reads name that role and read-scope requirement instead of only the generic grant/role/licence cause.
179
-
180
- `entra group list` defaults to compact properties (`id`, `displayName`, `mail`, `groupTypes`); `entra group show --id <group-id>` defaults to the richer reviewed group set including `isAssignableToRole`, which marks groups eligible for role assignment.
181
- Group `--select` accepts the [reviewed group property set](src/entra-groups.ts); `--fields` must be a subset of the fetched selection.
182
- Role-assignable membership changes need role-management permission and are refused by the membership write; that grant belongs to a later slice, never to this command.
183
- `entra group member add --group <group-id> --user <user-id>` previews adding one user to one non-role-assignable security or Microsoft 365 group through a directoryObjects reference.
184
- Both identifiers must be GUID object IDs; UPNs are not resolved, and only v1.0 is supported.
185
- Role-assignable, dynamic-membership and distribution groups are refused.
186
- The command verifies the user through `/users/<user-id>` before preview and again before sending; this read needs delegated User.ReadBasic.All or application User.Read.All, and the group and membership reads need D/A GroupMember.Read.All.
187
- The command needs a hand-enabled profile whose writes allow `mg.entra.group.member.add`; the mutation needs D/A GroupMember.ReadWrite.All, and delegated callers additionally need a groups role such as Groups Administrator.
188
- Delegated `--scopes` overrides only the mutation scope, which defaults to `https://graph.microsoft.com/GroupMember.ReadWrite.All`; application profiles reject `--scopes` and use their configured Graph `.default` audience.
189
- Without `--execute`, the command previews without journaling; `--execute --confirm <group-id>` sends once after a fresh read.
190
- An already-member user is a no-op without journaling when found by the initial read; a duplicate-reference 400 after sending is a journaled no-op.
191
- Execution reserves a journaled intent before the fresh read and records its outcome; uncertain outcomes require reading back the membership before any further action, with no automatic retry or replay.
192
- `entra group member list --group <group-id>` lists direct members and `entra group member-of list --group <group-id>` lists direct memberships; `--transitive` selects the flat nested closure instead.
193
- Relationship rows default to `id` and `displayName`; `--select` accepts only `id`, `displayName` and `mail`, and `--fields` must be a subset of that selection.
194
- Returned `@odata.type` stays visible alongside any `--fields` projection.
195
- Group lists return `groups`, member lists return `members`, parent-membership lists return `memberOf`, and single-group reads return `group`.
196
- Group and relationship lists report uniform totals through the shared list-totals helper: `total` holds the server-supplied `@odata.count` when the query carries `$count` (filtered reads) and `null` otherwise, beside a `count` line such as `3 of 10 groups`, `3 groups` or, when the total is unknown and more pages remain, `3 groups shown, more available`.
197
- The same helper covers the other wired core-directory lists: users, applications, service principals, owners, devices, administrative units and unit members, contacts, direct reports and memberships, directory roles, directory-role templates, role members, scoped members, scoped role memberships, role assignments and PIM eligible/active assignments, group PIM assignment/eligibility schedules, instances and eligibility requests, directory objects, deleted items, domains and DNS records, contracts, organizations and branding localizations, subscriptions, access-review definitions, instances, decisions, contacted reviewers and stages, sign-ins and directory audits, authentication methods and registration reports, PKI configurations and certificate authorities, Conditional Access policies, named locations, authentication-strength policies, combination configurations, authentication method modes, templates, authentication contexts, deleted policies and deleted named locations, attribute sets, custom-security-attribute definitions and allowed values, data-policy operations, delegated-admin customers, relationships and navigation lists, entitlement catalogs, access packages, assignment policies, resource-role scopes, assignments and assignment requests, federation configurations, oauth2 grants, app-role assignments and app-role-assigned-to, federated credentials, group lifecycle policies and setting templates, identity providers, lifecycle workflows, workflow templates, task definitions, runs, processing results and task reports, multi-tenant-organization tenants, on-premises synchronizations, risky users, risk detections, risky service principals and their history and detections, fraud-protection, web-application-firewall and verification providers, and agreements and acceptances. Every entra list command now reports through the same helper; only the dedicated `count` and `available-types` commands keep their `count` aggregates.
198
- `complete` names pagination state, with `reason` and an opaque `cursor` on partial reads.
199
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to group and relationship reads.
200
- Resume relationships with the same `--group` and direct or `--transitive` command, profile, scopes and API version; omit or repeat the original server query flags, and repeat local `--fields` and `--full` when wanted.
201
- Help warns that hidden members are omitted without `Member.Read.Hidden`; completion describes pagination, not visibility.
202
- Rows without non-null selected descriptive properties are preserved and reported as possibly limited by consent or unset properties; null properties stay null.
203
- Direct member results always carry the [v1.0 service-principal limitation](docs/graph-coverage.md#licensing-and-completeness-findings) warning; there is no silent beta or expansion fallback.
204
- `--filter` on group collections is sent with `$count=true` and `ConsistencyLevel: eventual`, which the relationship endpoints require.
205
- Delegated group reads default to `https://graph.microsoft.com/GroupMember.Read.All`; hidden members need `Member.Read.Hidden` and richer group properties may need `Group.Read.All`, while application profiles use the configured `.default` audience.
206
- When richer group access is needed, pass `--scopes https://graph.microsoft.com/Group.Read.All`; for hidden-member access, explicitly log in and read with `--scopes https://graph.microsoft.com/GroupMember.Read.All,https://graph.microsoft.com/Member.Read.Hidden` and satisfy the operation's delegated role requirements.
207
-
208
- Read directory roles, templates, members, scoped memberships, current assignments and PIM activity through twelve views; delegated profiles first need explicit login with the read scopes:
209
-
210
- ```sh
211
- mg-axi login --profile soc --scopes https://graph.microsoft.com/RoleManagement.Read.Directory,https://graph.microsoft.com/RoleEligibilitySchedule.Read.Directory,https://graph.microsoft.com/RoleAssignmentSchedule.Read.Directory
212
- mg-axi entra directory-role list --profile soc --limit 10
213
- mg-axi entra directory-role show --profile soc --id <role-id>
214
- mg-axi entra directory-role-template list --profile soc
215
- mg-axi entra directory-role-template show --profile soc --id <template-id>
216
- mg-axi entra directory-role member list --profile soc --role <role-id>
217
- mg-axi entra directory-role scoped-member list --profile soc --role <role-id>
218
- mg-axi entra directory-role scoped-member show --profile soc --role <role-id> --id <scoped-membership-id>
219
- mg-axi entra scoped-role-membership list --profile soc
220
- mg-axi entra scoped-role-membership show --profile soc --id <scoped-membership-id>
221
- mg-axi entra role-assignment list --profile soc --filter "principalId eq '<principal-id>'"
222
- mg-axi entra pim eligible list --profile soc
223
- mg-axi entra pim active list --profile soc --filter "assignmentType eq 'Activated'"
224
- ```
225
-
226
- `entra directory-role list` defaults to `id`, `displayName`, `description` and `roleTemplateId`; `entra directory-role show --id <role-id>` defaults to the full reviewed role set.
227
- Directory roles are activated instances only: a role appears after activation, never before, so an empty result never proves the role does not exist.
228
- `entra directory-role-template list` and `show` default to `id`, `displayName` and `description`; templates describe every role definition whether or not the role is activated.
229
- `entra directory-role member list --role <role-id>` defaults to `id` and `displayName` and carries each member kind in `@odata.type` (user, group, device or service principal); request `mail` or `userPrincipalName` with `--select` for richer member rows.
230
- `entra directory-role scoped-member list` and `show` and `entra scoped-role-membership list` and `show` default to `id`, `principalId`, `roleId` and `directoryScopeId` with the full reviewed set (`administrativeUnitId` included) on show; administrative-unit scopes need P1.
231
- `entra role-assignment list` defaults to `id`, `principalId`, `roleDefinitionId` and `directoryScopeId` and returns the current assignment inventory, including direct and PIM-activated assignments.
232
- Use `entra pim active list` to classify those assignments as described below.
233
- `entra pim eligible list` defaults to `id`, `principalId`, `roleDefinitionId` and `memberType`; it covers PIM-eligible assignments, which are not active, and eligible instances carry no `assignmentType`.
234
- `entra pim active list` defaults to those properties plus `assignmentType` and covers both directly assigned (`assignmentType` Assigned) and activated eligible (`assignmentType` Activated) assignments; `memberType` names how the instance reaches the principal.
235
- For built-in roles the unified `roleDefinitionId` matches the directory-role `roleTemplateId`.
236
- `--select` requests properties from the [reviewed role property sets](src/entra-roles.ts); `--fields` must be a subset of the fetched selection.
237
- `--filter` passes through as plain `$filter` with no `$count` or `ConsistencyLevel` contract; the named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply.
238
- Delegated directory-role, template, member, scoped-member, scoped-membership and role-assignment reads default to `https://graph.microsoft.com/RoleManagement.Read.Directory`; eligible PIM reads default to `https://graph.microsoft.com/RoleEligibilitySchedule.Read.Directory` and active PIM reads to `https://graph.microsoft.com/RoleAssignmentSchedule.Read.Directory`, while application profiles use the configured `.default` audience.
239
- Delegated callers additionally need a supported directory role per operation (for example Privileged Role Administrator, Global Reader or Security Reader for role reads, Directory Readers for assignments, Security Operator for PIM reads).
240
- Denied reads name that role requirement instead of only the generic grant/role/licence cause.
241
- Built-in roles are base inventory and custom role assignments need P1; administrative-unit-scoped memberships need P1; PIM reads need P2 or ID Governance.
242
- Role assignment, activation and every other PIM mutation belongs to later write slices, never to these reads.
243
-
244
- Read PIM-for-Groups assignment and eligibility through ten views; delegated profiles first need explicit login with the read scopes:
245
-
246
- ```sh
247
- mg-axi login --profile soc --scopes https://graph.microsoft.com/PrivilegedAssignmentSchedule.Read.AzureADGroup,https://graph.microsoft.com/PrivilegedEligibilitySchedule.Read.AzureADGroup
248
- mg-axi entra pim group-assignment-schedule list --profile soc --filter "groupId eq '<group-id>'"
249
- mg-axi entra pim group-assignment-schedule show --profile soc --id <assignment-schedule-id>
250
- mg-axi entra pim group-assignment-instance list --profile soc --filter "principalId eq '<principal-id>'"
251
- mg-axi entra pim group-assignment-instance show --profile soc --id <assignment-instance-id>
252
- mg-axi entra pim group-eligibility-schedule list --profile soc --filter "groupId eq '<group-id>'"
253
- mg-axi entra pim group-eligibility-schedule show --profile soc --id <eligibility-schedule-id>
254
- mg-axi entra pim group-eligibility-instance list --profile soc --filter "groupId eq '<group-id>'"
255
- mg-axi entra pim group-eligibility-instance show --profile soc --id <eligibility-instance-id>
256
- mg-axi entra pim group-eligibility-request list --profile soc --filter "groupId eq '<group-id>'"
257
- mg-axi entra pim group-eligibility-request show --profile soc --id <eligibility-request-id>
258
- ```
259
-
260
- Assignment schedules govern group membership or ownership over time and instances are the provisioned windows, never the schedule itself.
261
- Eligibility schedules govern who may activate and are not active grants, while eligibility instances are the provisioned eligibility windows and requests carry the ask plus its outcome.
262
- Every list requires `--filter` with an `eq` clause on `groupId` or `principalId` scoping the list to one group or principal, and the requirement fails validation before credentials.
263
- Assignment schedule and instance lists default to `id`, `principalId`, `groupId`, `accessId` and `assignmentType`; eligibility schedule and instance lists default to `id`, `principalId`, `groupId`, `accessId` and `memberType`; eligibility-request lists default to `id`, `action`, `status`, `principalId`, `groupId` and `accessId`.
264
- Every show defaults to its full reviewed property set, except eligibility-request show omits `justification` unless an explicit `--select` names it.
265
- `--select` requests properties from the [reviewed group PIM property sets](src/entra-group-pim.ts); `--fields` must be a subset of the fetched selection.
266
- `--filter` passes through as plain `$filter` with no `$count` or `ConsistencyLevel` contract; these collections document no `$count`, so the uniform `count` totals name the returned rows with a `null` total unless a page carries a server total.
267
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply; resume a list with the same `--filter`, profile, scopes and API version.
268
- Delegated assignment reads default to `https://graph.microsoft.com/PrivilegedAssignmentSchedule.Read.AzureADGroup` and eligibility reads to `https://graph.microsoft.com/PrivilegedEligibilitySchedule.Read.AzureADGroup`, while application profiles use the configured `.default` audience and reject `--scopes`.
269
- Delegated callers additionally need owner or member of the group or a supported directory role scoped at directory level (Global Reader or Privileged Role Administrator for role-assignable groups; otherwise Global Reader, Directory Writer, Groups Administrator, Identity Governance Administrator or User Administrator).
270
- Denied reads name that scope, role and licensing requirement instead of only the generic grant/role/licence cause.
271
- PIM for Groups needs P2 or ID Governance, and delegated personal Microsoft accounts are not supported.
272
- Assignment requests, approvals, `filterByCurrentUser` functions, group/principal navigation, resources, every count/ref/cast tail, beta and all mutations stay out; assignment requests stay out because their least privilege is the write scope `PrivilegedAssignmentSchedule.ReadWrite.AzureADGroup`.
273
-
274
- Read access reviews through ten views; delegated profiles first need explicit login with the read scope:
275
-
276
- ```sh
277
- mg-axi login --profile soc --scopes https://graph.microsoft.com/AccessReview.Read.All
278
- mg-axi entra access-review definition list --profile soc --limit 10
279
- mg-axi entra access-review definition list --profile soc --filter "status eq 'InProgress'"
280
- mg-axi entra access-review definition show --profile soc --id <definition-id>
281
- mg-axi entra access-review instance list --profile soc --definition <definition-id>
282
- mg-axi entra access-review instance show --profile soc --definition <definition-id> --id <instance-id>
283
- mg-axi entra access-review decision list --profile soc --definition <definition-id> --instance <instance-id>
284
- mg-axi entra access-review decision show --profile soc --definition <definition-id> --instance <instance-id> --id <decision-id>
285
- mg-axi entra access-review contacted-reviewer list --profile soc --definition <definition-id> --instance <instance-id>
286
- mg-axi entra access-review contacted-reviewer show --profile soc --definition <definition-id> --instance <instance-id> --id <reviewer-id>
287
- mg-axi entra access-review stage list --profile soc --definition <definition-id> --instance <instance-id>
288
- mg-axi entra access-review stage show --profile soc --definition <definition-id> --instance <instance-id> --id <stage-id>
289
- ```
290
-
291
- Definitions are review schedules (a series) and never carry their occurrences: each recurrence creates one instance per reviewed resource, and a one-time review creates one instance per resource.
292
- Instances are occurrences of one definition schedule, and each reviewed principal or resource in an instance carries one decision item.
293
- `entra access-review definition list` defaults to `id`, `displayName` and `status`; `entra access-review definition show --id <definition-id>` defaults to the full reviewed schedule set.
294
- `entra access-review instance list --definition <definition-id>` defaults to `id`, `status`, `startDateTime` and `endDateTime`; `entra access-review instance show` takes both `--definition` and `--id` and defaults to the full reviewed occurrence set.
295
- `entra access-review decision list --definition <definition-id> --instance <instance-id>` defaults to `id`, `accessReviewId`, `decision` and `recommendation`, where `accessReviewId` names the parent instance; `entra access-review decision show` takes `--definition`, `--instance` and `--id` and defaults to the full reviewed outcome set.
296
- `entra access-review contacted-reviewer list --definition <definition-id> --instance <instance-id>` defaults to `id`, `displayName` and `userPrincipalName`; contacted reviewers are reviewer identities recorded on one instance, whether or not notified, never review outcomes.
297
- `entra access-review stage list --definition <definition-id> --instance <instance-id>` defaults to `id`, `status`, `startDateTime` and `endDateTime`; stages are sequential phases of one instance (up to three when the definition sets `stageSettings`), and an instance without `stageSettings` has no stages.
298
- `durationInDays` is not a stage property; per-stage decisions belong to a later slice.
299
- Contacted-reviewer and stage `show` commands take `--definition`, `--instance` and `--id` and default to their full [reviewed property sets](src/entra-access-reviews.ts).
300
- Decision reads are read-only: listing never approves, denies or applies anything, and submitting or stopping a review belongs to a later slice, never to these reads.
301
- `--select` requests properties from the [reviewed access-review property sets](src/entra-access-reviews.ts); `--fields` must be a subset of the fetched selection.
302
- `--filter` passes through as plain `$filter` with no `$count` or `ConsistencyLevel` contract; on definitions only `contains()` over the scope query and `eq` on status are documented, on stages only `eq` is documented.
303
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to access-review reads.
304
- Resume any access-review list with `--cursor -` and supply the returned cursor on stdin, for example `mg-axi entra access-review definition list --profile soc --cursor - < cursor.txt`; small cursors can also use `--cursor <token>`.
305
- Resume instance, decision, contacted-reviewer and stage lists with the same `--definition` (and `--instance`), profile, scopes and API version; a cursor from another definition or instance fails validation instead of returning foreign rows.
306
- To replay a resumed result with `--full`, supply the original input cursor on stdin; the returned cursor continues after that result.
307
- Delegated reads default to `https://graph.microsoft.com/AccessReview.Read.All`, while application profiles use the configured `.default` audience.
308
- See the [access-review scope decisions](docs/coverage.md#ext-02-access-review-scope-decisions) for deferred legacy/unified reads and blocked history reads.
309
- Delegated callers additionally need a supported Entra role per review scope: group or app reviews need the review creator, Global Reader, Security Reader, User Administrator, Identity Governance Administrator or Security Administrator, while Entra-role reviews need Security Reader, Identity Governance Administrator, Privileged Role Administrator or Security Administrator.
310
- Denied reads name that role requirement instead of only the generic grant/role/licence cause.
311
- Access reviews need P2 or ID Governance depending on capability, not one uniform licence, and delegated personal Microsoft accounts are not supported.
312
-
313
- Read entitlement catalogs and access packages through twelve views; delegated profiles first need explicit login with the read scope:
314
-
315
- ```sh
316
- mg-axi login --profile soc --scopes https://graph.microsoft.com/EntitlementManagement.Read.All
317
- mg-axi entra entitlement catalog list --profile soc --limit 10
318
- mg-axi entra entitlement catalog list --profile soc --filter "state eq 'Published'"
319
- mg-axi entra entitlement catalog show --profile soc --id <catalog-id>
320
- mg-axi entra entitlement catalog count --profile soc
321
- mg-axi entra entitlement access-package list --profile soc --limit 10
322
- mg-axi entra entitlement access-package show --profile soc --id <access-package-id>
323
- mg-axi entra entitlement access-package count --profile soc
324
- mg-axi entra entitlement assignment-policy list --profile soc --access-package <access-package-id>
325
- mg-axi entra entitlement assignment-policy show --profile soc --access-package <access-package-id> --id <policy-id>
326
- mg-axi entra entitlement assignment-policy count --profile soc --access-package <access-package-id>
327
- mg-axi entra entitlement resource-role-scope list --profile soc --access-package <access-package-id>
328
- mg-axi entra entitlement resource-role-scope show --profile soc --access-package <access-package-id> --id <scope-id>
329
- mg-axi entra entitlement resource-role-scope count --profile soc --access-package <access-package-id>
330
- ```
331
-
332
- Catalogs are package containers and never carry their access packages: `entra entitlement catalog list` defaults to `id`, `displayName`, `state` and `catalogType`, and `entra entitlement catalog show --id <catalog-id>` defaults to the full reviewed container set.
333
- Access packages are the assignable bundles: `entra entitlement access-package list` defaults to `id`, `displayName` and `isHidden`, and `entra entitlement access-package show --id <access-package-id>` defaults to the full reviewed bundle set.
334
- `entra entitlement assignment-policy list --access-package <access-package-id>` defaults to `id`, `displayName` and `allowedTargetScope`: policies name who may request and how approval and review run, and `entra entitlement assignment-policy show` takes `--access-package` and `--id` and defaults to the full reviewed request, approval and review set.
335
- `entra entitlement resource-role-scope list --access-package <access-package-id>` defaults to `id` and `createdDateTime`: role-plus-scope pairs a package grants, and `entra entitlement resource-role-scope show` takes `--access-package` and `--id`.
336
- Approval and subject reads carry personal data and belong to later parts, never to these reads; questions, custom-extension stages and the linked role/scope detail need `$expand` and belong to later slices.
337
- `--select` requests properties from the [reviewed entitlement property sets](src/entra-entitlement-management.ts); `--fields` must be a subset of the fetched selection.
338
- `--filter` passes through as plain `$filter` with no `$count` or `ConsistencyLevel` contract.
339
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to entitlement reads.
340
- Resume any entitlement list with `--cursor -` and supply the returned cursor on stdin; resume policy and role-scope lists with the same `--access-package`, profile, scopes and API version.
341
- Delegated reads default to `https://graph.microsoft.com/EntitlementManagement.Read.All`, while application profiles use the configured `.default` audience.
342
- See the [entitlement-management scope decisions](docs/coverage.md#ext-02-entitlement-management-scope-decisions) for the deferred personal-data part and scheduled beta reads.
343
- Delegated callers additionally need a supported Entra role with catalog visibility (Global Reader and Identity Governance Administrator are among the supported roles).
344
- Denied reads name that role requirement instead of only the generic grant/role/licence cause.
345
- Entitlement management needs P2 or ID Governance depending on capability, not one uniform licence, and delegated personal Microsoft accounts are not supported.
346
-
347
- Read assignments and assignment requests through six views; they use the same scope default and login as the twelve reads above:
348
-
349
- ```sh
350
- mg-axi entra entitlement assignment list --profile soc --limit 10
351
- mg-axi entra entitlement assignment list --profile soc --filter "state eq 'Delivered'"
352
- mg-axi entra entitlement assignment show --profile soc --id <assignment-id>
353
- mg-axi entra entitlement assignment count --profile soc
354
- mg-axi entra entitlement assignment-request list --profile soc --limit 10
355
- mg-axi entra entitlement assignment-request show --profile soc --id <request-id>
356
- mg-axi entra entitlement assignment-request count --profile soc
357
- ```
358
-
359
- The wire resources carry no target or access-package linkage as scalars, so each list and show sends one fixed documented `$expand` set in code (target and accessPackage for assignments; accessPackage and assignment for requests) and projects only flattened linkage identifiers: `entra entitlement assignment list` defaults to `id`, `state`, `targetId`, `targetDisplayName`, `accessPackageId`, `expiredDateTime` and `schedule`, and `entra entitlement assignment-request list` defaults to `id`, `requestType`, `state`, `accessPackageId`, `assignmentId`, `createdDateTime`, `completedDateTime` and `schedule`.
360
- There is no user-supplied `$expand` anywhere: `--select` names only reviewed scalar or flattened fields, and navigation names fail as unknown properties.
361
- Request justification text and answers are personal-data payloads and ride only behind an explicit `--select`, even on show; the requestor subject, the assignment policy link and the `filterByCurrentUser`/`additionalAccess` functions belong to later slices.
362
- Callers with only catalog-scoped roles must filter to one access package, for example `--filter "accessPackage/id eq '<access-package-id>'"`.
363
- Assignment and request reads never mutate: no request creation, cancellation, approval or reprocessing, and no beta.
364
- Denied assignment and request reads name the Catalog reader least-privileged role and the supported Entra roles instead of only the generic grant/role/licence cause.
365
-
366
- Read lifecycle workflows, workflow templates, task definitions and tenant settings through ten views; workflow reads default to the ReadBasic scope while template, task-definition and settings reads default to the broader read scope:
367
-
368
- ```sh
369
- mg-axi login --profile soc --scopes https://graph.microsoft.com/LifecycleWorkflows-Workflow.ReadBasic.All
370
- mg-axi entra lifecycle workflow list --profile soc --limit 10
371
- mg-axi entra lifecycle workflow list --profile soc --filter "category eq 'leaver'"
372
- mg-axi entra lifecycle workflow show --profile soc --id <workflow-id>
373
- mg-axi entra lifecycle workflow count --profile soc
374
- mg-axi login --profile soc --scopes https://graph.microsoft.com/LifecycleWorkflows.Read.All
375
- mg-axi entra lifecycle workflow-template list --profile soc --limit 10
376
- mg-axi entra lifecycle workflow-template show --profile soc --id <template-id>
377
- mg-axi entra lifecycle workflow-template count --profile soc
378
- mg-axi entra lifecycle task-definition list --profile soc --limit 10
379
- mg-axi entra lifecycle task-definition show --profile soc --id <task-definition-id>
380
- mg-axi entra lifecycle task-definition count --profile soc
381
- mg-axi entra lifecycle settings show --profile soc
382
- ```
383
-
384
- Read one workflow's runs and user/subject processing results through nine views; reporting reads default to the reports scope:
385
-
386
- ```sh
387
- mg-axi login --profile soc --scopes https://graph.microsoft.com/LifecycleWorkflows-Reports.Read.All
388
- mg-axi entra lifecycle run list --workflow <workflow-id> --profile soc --limit 10
389
- mg-axi entra lifecycle run list --workflow <workflow-id> --profile soc --filter "processingStatus eq 'completed'"
390
- mg-axi entra lifecycle run show --workflow <workflow-id> --profile soc --id <run-id>
391
- mg-axi entra lifecycle run count --workflow <workflow-id> --profile soc
392
- mg-axi entra lifecycle user-processing-result list --workflow <workflow-id> --profile soc --limit 10
393
- mg-axi entra lifecycle user-processing-result show --workflow <workflow-id> --profile soc --id <result-id>
394
- mg-axi entra lifecycle user-processing-result count --workflow <workflow-id> --profile soc
395
- mg-axi entra lifecycle subject-processing-result list --workflow <workflow-id> --profile soc --limit 10
396
- mg-axi entra lifecycle subject-processing-result show --workflow <workflow-id> --profile soc --id <result-id>
397
- mg-axi entra lifecycle subject-processing-result count --workflow <workflow-id> --profile soc
398
- ```
399
-
400
- Read one workflow's task reports through five views; task-report reads share the reports scope:
401
-
402
- ```sh
403
- mg-axi entra lifecycle task-report list --workflow <workflow-id> --profile soc --limit 10
404
- mg-axi entra lifecycle task-report list --workflow <workflow-id> --profile soc --filter "processingStatus eq 'completed'"
405
- mg-axi entra lifecycle task-report show --workflow <workflow-id> --profile soc --id <report-id>
406
- mg-axi entra lifecycle task-report count --workflow <workflow-id> --profile soc
407
- mg-axi entra lifecycle task-report task show --workflow <workflow-id> --report <report-id> --profile soc
408
- mg-axi entra lifecycle task-report task-definition show --workflow <workflow-id> --report <report-id> --profile soc
409
- ```
410
-
411
- Read one run's user, subject and task processing results through nine views; run-nested reads share the reports scope:
412
-
413
- ```sh
414
- mg-axi entra lifecycle run user-processing-result list --workflow <workflow-id> --run <run-id> --profile soc --limit 10
415
- mg-axi entra lifecycle run user-processing-result list --workflow <workflow-id> --run <run-id> --profile soc --filter "processingStatus eq 'completed'"
416
- mg-axi entra lifecycle run user-processing-result show --workflow <workflow-id> --run <run-id> --profile soc --id <result-id>
417
- mg-axi entra lifecycle run user-processing-result count --workflow <workflow-id> --run <run-id> --profile soc
418
- mg-axi entra lifecycle run subject-processing-result list --workflow <workflow-id> --run <run-id> --profile soc --limit 10
419
- mg-axi entra lifecycle run subject-processing-result show --workflow <workflow-id> --run <run-id> --profile soc --id <result-id>
420
- mg-axi entra lifecycle run subject-processing-result count --workflow <workflow-id> --run <run-id> --profile soc
421
- mg-axi entra lifecycle run task-processing-result list --workflow <workflow-id> --run <run-id> --profile soc --limit 10
422
- mg-axi entra lifecycle run task-processing-result show --workflow <workflow-id> --run <run-id> --profile soc --id <result-id>
423
- mg-axi entra lifecycle run task-processing-result count --workflow <workflow-id> --run <run-id> --profile soc
424
- ```
425
-
426
- `entra lifecycle run task-processing-result list` defaults to `id`, `processingStatus` and `failureReason`, and `entra lifecycle run task-processing-result show` defaults to the full reviewed scalar set including failure detail; task rows never carry subject, task or workflowSubject detail.
427
- Run user results share the top-level user query contract (`--select` reviewed, `--filter` plain), while run subject rows always arrive whole like their top-level counterparts (Graph documents no `$select` on the list and `$expand` only on the get) and the run task single has no operation-level page, so both arrive whole and `--fields` projects them locally; `--select` is refused before credentials.
428
- User, subject and task processing results are personal data: user and subject rows default to status and counts only (subject rows add `subjectType`), and the subject link is never requested.
429
- Third-level processing results, reprocessed runs, subject and task sub-reads and the time-windowed summary functions belong to later slices.
430
-
431
- `entra lifecycle task-report list` defaults to `id`, `runId`, `processingStatus`, `totalUsersCount`, `failedUsersCount` and `successfulUsersCount`, and `entra lifecycle task-report show` defaults to the full reviewed scalar set; report rows never carry task, task-definition or processing-result detail.
432
- The task-report get and the taskReport-nested task and taskDefinition singles document no query parameters, so those rows always arrive whole and `--fields` projects them locally; `--select` is refused before credentials.
433
- Third-level processing results, reprocessed runs, subject sub-reads and the time-windowed summary functions belong to later slices.
434
-
435
- `entra lifecycle run list` defaults to `id`, `processingStatus`, `totalUsersCount`, `failedUsersCount` and `successfulUsersCount`, and `entra lifecycle run show` defaults to the full reviewed scalar set; run rows never carry processing results.
436
- User and subject processing results are personal data: lists and singles default to status and counts only (subject lists add `subjectType`), and the subject link is never requested.
437
- The subject list documents `$filter` but not `$select` and the subject get documents `$expand` only, so subject rows always arrive whole and `--fields` projects them locally; `--select` is refused before credentials.
438
- Third-level processing results, taskProcessingResults sub-reads under a user or subject result, reprocessed runs, subject sub-reads and the time-windowed summary functions belong to later slices.
439
-
440
- `entra lifecycle workflow list` defaults to `id`, `displayName`, `category`, `isEnabled` and `isSchedulingEnabled`, and `entra lifecycle workflow show --id <workflow-id>` defaults to the full reviewed scalar set.
441
- Workflow rows never carry tasks, runs, processing results or task reports: tasks ride expanded by default on the get and are dropped in local projection, and third-level processing results and summaries belong to later parts, never to these reads.
442
- `entra lifecycle workflow-template list` defaults to `id`, `displayName` and `category`; the template get documents no query parameters, so `entra lifecycle workflow-template show --id <template-id>` takes no `--select` and always returns whole rows.
443
- `entra lifecycle task-definition list` defaults to `id`, `displayName`, `category` and `version`, and `entra lifecycle task-definition show --id <task-definition-id>` defaults to the full reviewed parameter set.
444
- `entra lifecycle settings show` returns the tenant schedule interval with the email and quarantine configuration; updating settings belongs to no read slice.
445
- `--select` requests properties from the [reviewed lifecycle property sets](src/entra-lifecycle-workflows.ts); `--fields` must be a subset of the fetched selection, and navigation names (`tasks`, `createdBy`, `lastModifiedBy`, `previewScope`) fail as unknown properties.
446
- `--filter` passes through as plain `$filter` with no `$count` or `ConsistencyLevel` contract; `$search`, `$orderby` and `$expand` stay unreviewed.
447
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to lifecycle reads.
448
- Resume any lifecycle list with `--cursor -` and supply the returned cursor on stdin.
449
- Delegated workflow reads default to `https://graph.microsoft.com/LifecycleWorkflows-Workflow.ReadBasic.All` (richer detail may need `LifecycleWorkflows-Workflow.Read.All` or `LifecycleWorkflows.Read.All`), template, task-definition and settings reads to `https://graph.microsoft.com/LifecycleWorkflows.Read.All`, and run, processing-result and task-report reads to `https://graph.microsoft.com/LifecycleWorkflows-Reports.Read.All` (richer detail may need `LifecycleWorkflows.Read.All` or `LifecycleWorkflows.ReadWrite.All`), while application profiles use the configured `.default` audience.
450
- See the [lifecycle-workflows scope decisions](docs/coverage.md#ext-02-lifecycle-workflows-scope-decisions) for the deferred nested-result and summary parts.
451
- Delegated callers additionally need Global Reader or Lifecycle Workflows Administrator.
452
- Denied reads name that scope, role and licensing requirement instead of only the generic grant/role/licence cause.
453
- Lifecycle workflows need Microsoft Entra ID Governance or Microsoft Entra Suite (every governed user, not only administrators), and delegated personal Microsoft accounts are not supported.
454
-
455
- Log in with `https://graph.microsoft.com/Device.Read.All` or `https://graph.microsoft.com/AdministrativeUnit.Read.All`, then inspect directory devices and administrative units:
456
-
457
- ```sh
458
- mg-axi login --profile soc --scopes https://graph.microsoft.com/Device.Read.All
459
- mg-axi entra device list --profile soc --limit 10
460
- mg-axi entra device show --profile soc --id <device-id>
461
- mg-axi login --profile soc --scopes https://graph.microsoft.com/AdministrativeUnit.Read.All
462
- mg-axi entra administrative-unit list --profile soc
463
- mg-axi entra administrative-unit show --profile soc --id <administrative-unit-id>
464
- mg-axi entra administrative-unit member list --profile soc --administrative-unit <administrative-unit-id>
465
- ```
466
-
467
- `entra device list` defaults to compact properties (`id`, `displayName`, `operatingSystem`, `accountEnabled`); `entra device show --id <device-id>` takes the object `id`, not `deviceId`, and defaults to the full reviewed device set.
468
- Directory devices are Entra directory objects; Intune managed devices and device actions are a separately authorized surface, never these commands.
469
- `entra administrative-unit list` defaults to `id`, `displayName`, `visibility` and `membershipType`; `entra administrative-unit show --id <administrative-unit-id>` defaults to the full reviewed unit set including the membership rule.
470
- Device and unit `--select` accept their [reviewed property sets](src/entra-directory.ts); `--fields` must be a subset of the fetched selection.
471
- Unit show adds a licensing hint when the projected `membershipType` is `Dynamic`; custom `--select` or `--fields` that omit it also omit the hint.
472
- See the [licence matrix](docs/graph-coverage.md#licence-matrix-by-area) for device and administrative-unit licensing requirements.
473
- `entra administrative-unit member list --administrative-unit <administrative-unit-id>` lists member users, groups and devices with the same `id`/`displayName`/`mail` selection, `@odata.type` preservation, hidden-membership and limited-information behavior as group relationships.
474
- Device and unit lists return `devices` and `administrativeUnits`, member lists return `members`, and single-object reads return `device` and `administrativeUnit`.
475
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to device and administrative-unit reads.
476
- Resume unit-member lists with the same `--administrative-unit`, profile, scopes and API version; omit or repeat the original server query flags, and repeat local `--fields` and `--full` when wanted.
477
- Delegated device reads default to `https://graph.microsoft.com/Device.Read.All` and unit reads to `https://graph.microsoft.com/AdministrativeUnit.Read.All`, while application profiles use the configured `.default` audience; hidden unit memberships need `Member.Read.Hidden`.
478
- Denied directory reads return operation-specific permission, delegated-role and licensing guidance rather than empty results; HTTP 403 alone does not identify which prerequisite is missing.
479
- `--filter` on these collections is sent with `$count=true` and `ConsistencyLevel: eventual`.
480
-
481
- Log in with `https://graph.microsoft.com/Organization.Read.All` for organization reads and `https://graph.microsoft.com/User.Read` for branding reads, then inspect the tenant and its sign-in branding:
482
-
483
- ```sh
484
- mg-axi login --profile soc --scopes https://graph.microsoft.com/Organization.Read.All,https://graph.microsoft.com/User.Read
485
- mg-axi entra organization list --profile soc
486
- mg-axi entra organization show --profile soc --id <organization-id>
487
- mg-axi entra organization branding show --profile soc --organization <organization-id>
488
- mg-axi entra organization branding-localization list --profile soc --organization <organization-id>
489
- mg-axi entra organization branding-localization show --profile soc --organization <organization-id> --id fr-FR
490
- ```
491
-
492
- `entra organization list` defaults to compact properties (`id`, `displayName`, `tenantType`, `verifiedDomains`); exactly one organization exists per tenant.
493
- `entra organization show --id <organization-id>` defaults to the full reviewed organization set including technical notification mails and the privacy profile.
494
- `--select` requests properties from the [reviewed organization and branding field sets](src/entra-organization.ts); `--fields` projects locally and must be a subset of the fetched selection.
495
- Delegated organization reads default to `https://graph.microsoft.com/Organization.Read.All` for full metadata; delegated `User.Read` returns only `id`, `displayName` and `verifiedDomains` with every other property null.
496
- `entra organization branding show --organization <organization-id>` reads the default branding metadata (non-Stream text and URLs); the session sends the documented `Accept-Language: 0` header and locale variants come from the localizations collection.
497
- Branding leaves default to delegated `https://graph.microsoft.com/User.Read`, the documented least-privileged scope; `OrganizationalBranding.Read.All` is the purpose-built alternative and `Organization.Read.All` also works when passed as `--scopes`.
498
- Application profiles need admin-consented `Organization.Read.All` for organization metadata or `OrganizationalBranding.Read.All` for branding, using the configured Graph `.default` audience.
499
- Stream image properties (`bannerLogo`, `backgroundImage` and friends) are refused before credentials: they need a later piece with its own binary-output contract.
500
- A branding 404 may indicate unconfigured branding, a missing locale, or a missing or inaccessible organization; configuring custom branding needs P1/P2, and contact fields on the organization are personal data.
501
- Delegated callers additionally need a supported Entra role (Directory Readers or Global Reader for organizations; Global Reader or Organizational Branding Administrator for branding); personal Microsoft accounts are not supported.
502
- Organization and localization lists return `organizations` and `brandingLocalizations`, single-object reads return `organization`, `branding` and `brandingLocalization`.
503
- Localization lists default to `id`, `signInPageText`, `usernameHintText` and `backgroundColor`; branding and localization show commands default to the full reviewed non-Stream field set.
504
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to organization and branding reads.
505
- All five named reads default to `--api-version v1.0`; explicit `--api-version beta` requires a preview-enabled profile, with no automatic fallback.
506
- Organization and localization lists offer no `--filter`: Graph documents `$select` only on these routes, so the flag is refused before credentials.
507
- Denied organization and branding reads name the scope, role and licensing guidance instead of only the generic cause.
508
- No organization mutation lives here; organization-scoped certificate-based-auth configuration, extensions, beta-only settings and the POST lookup actions belong to later pieces; see the [organization scope decisions](docs/coverage.md#ext-01-organization-scope-decisions) for deferred reads and later subfamilies.
509
-
510
- Log in with `https://graph.microsoft.com/Directory.Read.All`, then inspect group lifecycle policies:
511
-
512
- ```sh
513
- mg-axi login --profile soc --scopes https://graph.microsoft.com/Directory.Read.All
514
- mg-axi entra group-lifecycle-policy list --profile soc
515
- mg-axi entra group-lifecycle-policy show --profile soc --id <policy-id>
516
- mg-axi entra group-lifecycle-policy count --profile soc
517
- ```
518
-
519
- Log in with `https://graph.microsoft.com/GroupSettings.Read.All`, then inspect group setting templates:
520
-
521
- ```sh
522
- mg-axi login --profile soc --scopes https://graph.microsoft.com/GroupSettings.Read.All
523
- mg-axi entra group-setting-template list --profile soc
524
- mg-axi entra group-setting-template show --profile soc --id <template-id>
525
- mg-axi entra group-setting-template count --profile soc
526
- ```
527
-
528
- `entra group-lifecycle-policy list` defaults to compact properties (`id`, `groupLifetimeInDays`, `managedGroupTypes`); `entra group-lifecycle-policy show --id <policy-id>` defaults to the full reviewed lifecycle set (`id`, `alternateNotificationEmails`, `groupLifetimeInDays`, `managedGroupTypes`).
529
- `entra group-setting-template list` defaults to compact properties (`id`, `displayName`, `description`); `entra group-setting-template show --id <template-id>` defaults to the full reviewed template set (`id`, `deletedDateTime`, `displayName`, `description`, `values`).
530
- `--select` requests properties from the [reviewed group lifecycle field sets](src/entra-group-lifecycle.ts); `--fields` projects locally and must be a subset of the fetched selection.
531
- Lifecycle-policy lists accept `--filter` as plain `$filter` without adding `$count=true` or `ConsistencyLevel`; template lists offer no `--filter` because Graph documents `$select` only for `/groupSettingTemplates`.
532
- Each `count` returns one scalar (`groupLifecyclePolicyCount`, `groupSettingTemplateCount`) from its text/plain `$count` route and takes no `--filter`, `--select`, `--limit` or `--cursor`.
533
- Raw `api get` supports `/groupLifecyclePolicies`, `/groupLifecyclePolicies/<policy-id>`, `/groupSettingTemplates` and `/groupSettingTemplates/<template-id>`; the `$count` routes are available only through the named count commands.
534
- Lifecycle-policy lists return `groupLifecyclePolicies`, single-policy reads return `groupLifecyclePolicy`, template lists return `groupSettingTemplates` and single-template reads return `groupSettingTemplate`.
535
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to these reads.
536
- Named reads truncate top-level strings and strings in string arrays at 500 characters with a `--full` recovery hint; nested objects in template `values` pass through without text truncation.
537
- All six named reads support only `--api-version v1.0`; `--api-version beta` fails validation before credentials, including on preview-enabled profiles.
538
- Delegated lifecycle-policy reads default to `https://graph.microsoft.com/Directory.Read.All` and delegated template reads default to `https://graph.microsoft.com/GroupSettings.Read.All`, while application profiles use the configured `.default` audience.
539
- Delegated template callers additionally need a supported Entra role (Directory Readers or Global Reader are the least-privileged roles); no delegated role prerequisite is stated for lifecycle-policy reads; personal Microsoft accounts are not supported on either family.
540
- No P1/P2 prerequisite is stated for these reads; denied reads name the scope, role and licensing guidance instead of only the generic cause.
541
- No lifecycle or template mutation lives here; `groupSettingTemplates/delta()`, beta policies and templates and the POST lookup actions belong to later pieces; see the [group lifecycle scope decisions](docs/coverage.md#ext-01-group-lifecycle-scope-decisions) for deferred reads and later subfamilies.
542
-
543
- Log in with `https://graph.microsoft.com/CustomSecAttributeDefinition.Read.All`, then inspect attribute sets, custom security attribute definitions and their allowed values:
544
-
545
- ```sh
546
- mg-axi login --profile soc --scopes https://graph.microsoft.com/CustomSecAttributeDefinition.Read.All
547
- mg-axi entra attribute-set list --profile soc
548
- mg-axi entra attribute-set show --profile soc --id <set-id>
549
- mg-axi entra attribute-set count --profile soc
550
- mg-axi entra custom-security-attribute-definition list --profile soc
551
- mg-axi entra custom-security-attribute-definition show --profile soc --id <definition-id>
552
- mg-axi entra custom-security-attribute-definition count --profile soc
553
- mg-axi entra allowed-value list --profile soc --definition <definition-id>
554
- mg-axi entra allowed-value show --profile soc --definition <definition-id> --id <value-id>
555
- mg-axi entra allowed-value count --profile soc --definition <definition-id>
556
- ```
557
-
558
- `entra attribute-set list` defaults to the reviewed set (`id`, `description`, `maxAttributesPerSet`); `entra custom-security-attribute-definition list` defaults to compact properties (`id`, `attributeSet`, `name`, `status`, `type`) while show defaults to the full reviewed definition set; `entra allowed-value list` defaults to the reviewed set (`id`, `isActive`).
559
- `--select` requests properties from the [reviewed custom-security field sets](src/entra-custom-security-attributes.ts); `--fields` projects locally and must be a subset of the fetched selection.
560
- Definition lists accept `--filter` as plain `$filter` (eq) without adding `$count=true` or `ConsistencyLevel`; attribute-set and allowed-value lists offer no `--filter` because Graph documents no `$filter` for those routes.
561
- Allowed-value lists and shows take `--definition <definition-id>`; definition lists never inline `allowedValues` (`$expand` is not reviewed here) and show points at the allowed-value list instead.
562
- Each `count` returns `count: { returned: <total>, complete: true }` from its text/plain `$count` route; only the definition count accepts `--filter` to narrow the total server-side, and no count takes `--select`, `--limit` or `--cursor`.
563
- Raw `api get` supports `/directory/attributeSets`, `/directory/attributeSets/<set-id>`, `/directory/customSecurityAttributeDefinitions`, `/directory/customSecurityAttributeDefinitions/<definition-id>`, `/directory/customSecurityAttributeDefinitions/<definition-id>/allowedValues` and the single allowed-value route; the `$count` routes are available only through the named count commands.
564
- Attribute-set lists return `attributeSets` and single-set reads return `attributeSet`; definition lists return `customSecurityAttributeDefinitions` and single-definition reads return `customSecurityAttributeDefinition`; allowed-value lists return `allowedValues` and single-value reads return `allowedValue`.
565
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to these reads.
566
- All nine named reads support only `--api-version v1.0`; `--api-version beta` fails validation before credentials, including on preview-enabled profiles.
567
- Delegated reads default to `https://graph.microsoft.com/CustomSecAttributeDefinition.Read.All`, while application profiles use the configured `.default` audience.
568
- Delegated callers additionally need a custom-security-attribute role even for Global Administrators (Attribute Definition Reader or Attribute Definition Administrator work for every read; some reads also accept Attribute Assignment Reader or Attribute Assignment Administrator); personal Microsoft accounts are not supported.
569
- No P1/P2 prerequisite is stated for these reads; denied reads name the scope, role and licensing guidance instead of only the generic cause.
570
- No custom-security-attribute mutation lives here; beta attribute sets, definitions and allowed values belong to a later piece; see the [custom-security-attributes scope decisions](docs/coverage.md#ext-01-custom-security-attributes-scope-decisions) for deferred reads and later subfamilies.
571
-
572
- Log in with `https://graph.microsoft.com/Directory.Read.All`, then inspect partner-tenant customer contracts:
573
-
574
- ```sh
575
- mg-axi login --profile soc --scopes https://graph.microsoft.com/Directory.Read.All
576
- mg-axi entra contract list --profile soc
577
- mg-axi entra contract show --profile soc --id <contract-id>
578
- mg-axi entra contract count --profile soc
579
- ```
580
-
581
- Contracts exist in partner tenants only (Cloud Solution Provider, Office 365 Syndication or Advisor programs); a non-partner tenant lists zero contracts, which is an answer rather than an error.
582
- `entra contract list` defaults to compact properties (`id`, `displayName`, `contractType`, `defaultDomainName`); `entra contract show --id <contract-id>` defaults to the full reviewed contract set (`contractType`, `customerId`, `defaultDomainName`, `displayName`, `id`).
583
- `--select` requests properties from the [reviewed contract field set](src/entra-contracts.ts); `--fields` projects locally and must be a subset of the fetched selection.
584
- Contract lists accept `--filter` as plain `$filter` without adding `$count=true` or `ConsistencyLevel`; filtering is documented for `customerId`, `defaultDomainName` and `displayName`.
585
- `entra contract count` returns one scalar (`contractCount`) from the text/plain `$count` route and takes no `--filter`, `--select`, `--limit` or `--cursor`.
586
- Raw `api get` supports `/contracts` and `/contracts/<contract-id>`; `/contracts/$count` is available only through the named count command.
587
- Contract lists return `contracts` and single-contract reads return `contract`.
588
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to contract reads.
589
- All three named reads support only `--api-version v1.0`; `--api-version beta` fails validation before credentials, including on preview-enabled profiles.
590
- Delegated contract reads default to `https://graph.microsoft.com/Directory.Read.All`, while application profiles use the configured `.default` audience.
591
- Delegated callers additionally need a supported Entra role (Directory Readers is the least-privileged role); personal Microsoft accounts are not supported.
592
- No P1/P2 prerequisite is stated for contract reads; denied reads name the scope, role, partner-tenant and licensing guidance instead of only the generic cause.
593
- No contract mutation lives here; `contracts/delta()`, beta contracts and the POST lookup actions belong to later pieces; see the [partner contracts scope decisions](docs/coverage.md#ext-04-partner-contracts-scope-decisions) for deferred reads and later subfamilies.
594
-
595
- Log in with `https://graph.microsoft.com/DelegatedAdminRelationship.Read.All`, then inspect partner-tenant delegated administration:
596
-
597
- ```sh
598
- mg-axi login --profile soc --scopes https://graph.microsoft.com/DelegatedAdminRelationship.Read.All
599
- mg-axi entra delegated-admin-customer list --profile soc
600
- mg-axi entra delegated-admin-customer show --profile soc --id <customer-id>
601
- mg-axi entra delegated-admin-relationship list --profile soc
602
- mg-axi entra delegated-admin-relationship show --profile soc --id <relationship-id>
603
- mg-axi entra delegated-admin-relationship list-access-assignments --profile soc --id <relationship-id>
604
- mg-axi entra delegated-admin-relationship show-access-assignment --profile soc --id <relationship-id> --assignment-id <assignment-id>
605
- mg-axi entra delegated-admin-relationship list-operations --profile soc --id <relationship-id>
606
- mg-axi entra delegated-admin-relationship show-operation --profile soc --id <relationship-id> --operation-id <operation-id>
607
- mg-axi entra delegated-admin-relationship list-requests --profile soc --id <relationship-id>
608
- mg-axi entra delegated-admin-relationship show-request --profile soc --id <relationship-id> --request-id <request-id>
609
- mg-axi entra delegated-admin-customer list-service-management-details --profile soc --id <customer-id>
610
- mg-axi entra delegated-admin-customer show-service-management-detail --profile soc --id <customer-id> --detail-id <detail-id>
611
- mg-axi entra delegated-admin-customer count --profile soc
612
- mg-axi entra delegated-admin-customer count-service-management-details --profile soc --id <customer-id>
613
- mg-axi entra delegated-admin-relationship count --profile soc
614
- mg-axi entra delegated-admin-relationship count-access-assignments --profile soc --id <relationship-id>
615
- mg-axi entra delegated-admin-relationship count-operations --profile soc --id <relationship-id>
616
- mg-axi entra delegated-admin-relationship count-requests --profile soc --id <relationship-id>
617
- ```
618
-
619
- Delegated-admin reads run in the partner tenant; customer objects are created by the system when a relationship exists and deleted when none remain, so a non-partner tenant lists zero customers, which is an answer rather than an error.
620
- `entra delegated-admin-customer list` defaults to compact properties (`id`, `displayName`, `tenantId`); `entra delegated-admin-relationship list` defaults to (`id`, `displayName`, `status`, `customer`, `endDateTime`); both top-level show commands default to the full reviewed property set.
621
- `--select` requests properties from the reviewed delegated-admin field sets; `--fields` projects locally and must be a subset of the fetched selection.
622
- Both top-level lists accept `--filter` as plain `$filter` without adding `$count=true` or `ConsistencyLevel`.
623
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to delegated-admin reads.
624
- The access-assignment, operation and request navigation lists bind their parent relationship through `--id` and take `--assignment-id`, `--operation-id` and `--request-id` on their shows; the service-management-detail reads bind their parent customer through `--id` and take `--detail-id` on the show.
625
- The navigation lists default to compact properties (`id`, `status`, `accessContainer`, `accessDetails` for assignments; `id`, `operationType`, `status`, `lastModifiedDateTime` for operations; `id`, `action`, `status`, `lastModifiedDateTime` for requests) while the service-management-detail reads always carry (`id`, `serviceName`, `serviceManagementUrl`); every show defaults to its full reviewed property set.
626
- The access-assignment, operation and request lists accept `--filter` as plain `$filter` without adding `$count=true` or `ConsistencyLevel`; Graph documents no query parameters for service-management details, so those two reads take no `--select` or `--filter` and project `--fields` locally.
627
- Each `count` returns one scalar (`delegatedAdminCustomerCount`, `delegatedAdminServiceManagementDetailCount`, `delegatedAdminRelationshipCount`, `delegatedAdminAccessAssignmentCount`, `delegatedAdminRelationshipOperationCount`, `delegatedAdminRelationshipRequestCount`) from its text/plain `$count` route and takes no `--filter`, `--select`, `--limit` or `--cursor`.
628
- All eighteen named reads support only `--api-version v1.0`; `--api-version beta` fails validation before credentials, including on preview-enabled profiles.
629
- Delegated delegated-admin reads default to `https://graph.microsoft.com/DelegatedAdminRelationship.Read.All`, while application profiles use the configured `.default` audience.
630
- Personal Microsoft accounts are not supported.
631
- No P1/P2 prerequisite is stated for delegated-admin reads; denied reads name the scope, partner-tenant and licensing guidance instead of only the generic cause.
632
- No delegated-admin mutation lives here; the container root belongs to a later piece, while the multi-tenant-organization reads shipped in the next section and tenant-lookup functions ship as `entra tenant-information show` in the tenant-information usage below; see the [delegated-admin scope decisions](docs/coverage.md#ext-04-delegated-admin-scope-decisions) for deferred reads and later subfamilies.
633
-
634
- Log in with `https://graph.microsoft.com/MultiTenantOrganization.Read.All`, then inspect the tenant multitenant organization:
635
-
636
- ```sh
637
- mg-axi login --profile soc --scopes https://graph.microsoft.com/MultiTenantOrganization.Read.All
638
- mg-axi entra multi-tenant-organization show --profile soc
639
- mg-axi entra multi-tenant-organization join-request show --profile soc
640
- mg-axi entra multi-tenant-organization tenant list --profile soc
641
- mg-axi entra multi-tenant-organization tenant count --profile soc
642
- ```
643
-
644
- At most one multitenant organization exists per tenant; a tenant outside any multitenant organization reads the container as state inactive with null properties and lists zero tenants, which is an answer rather than an error.
645
- `entra multi-tenant-organization tenant list` defaults to compact properties (`tenantId`, `displayName`, `role`, `state`); both show commands default to the full reviewed property set.
646
- `--select` requests properties from the reviewed multi-tenant-organization field sets; `--fields` projects locally and must be a subset of the fetched selection.
647
- The tenant list accepts `--filter` as plain `$filter` without adding `$count=true` or `ConsistencyLevel`.
648
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to multi-tenant-organization reads.
649
- All four named reads support only `--api-version v1.0`; `--api-version beta` fails validation before credentials, including on preview-enabled profiles.
650
- Delegated multi-tenant-organization reads default to `https://graph.microsoft.com/MultiTenantOrganization.Read.All` (the lower-privileged delegated `MultiTenantOrganization.ReadBasic.All` returns displayName and tenantId only) and additionally need Security Reader or Global Reader, while application profiles use the configured `.default` audience.
651
- Personal Microsoft accounts are not supported, and these reads run in the commercial Global service only.
652
- Multi-tenant-organization participation needs Entra ID P1; denied reads name the scope, roles and licensing guidance instead of only the generic cause.
653
- No multi-tenant-organization mutation lives here; the single-member read stays deferred because its documented least privilege is the write scope `MultiTenantOrganization.ReadWrite.All`, and tenant-lookup functions ship as `entra tenant-information show` in the tenant-information usage below; see the [multi-tenant-organization scope decisions](docs/coverage.md#ext-04-multi-tenant-organization-scope-decisions) for the deferred read.
3
+ <p align="center">
4
+ <a href="https://www.npmjs.com/package/@knowttl/mg-axi"><img alt="npm" src="https://img.shields.io/npm/v/@knowttl/mg-axi?style=flat-square" /></a>
5
+ <a href="https://github.com/kunchenguid/axi"><img alt="AXI" src="https://img.shields.io/badge/AXI-Agent%20eXperience%20Interface-green?style=flat-square" /></a>
6
+ </p>
654
7
 
655
- Log in with `https://graph.microsoft.com/CrossTenantInformation.ReadBasic.All`, then look up tenant information by domain name or tenant ID:
656
-
657
- ```sh
658
- mg-axi login --profile soc --scopes https://graph.microsoft.com/CrossTenantInformation.ReadBasic.All
659
- mg-axi entra tenant-information show --domain-name example.invalid --profile soc
660
- mg-axi entra tenant-information show --tenant-id <tenant-id> --profile soc
661
- ```
8
+ Microsoft Graph CLI for agents, designed with AXI (Agent eXperience Interface).
662
9
 
663
- `entra tenant-information show` needs exactly one of `--domain-name` or `--tenant-id`; combining them or omitting both fails validation before credentials.
664
- Both lookups return the full reviewed tenantInformation set (`defaultDomainName`, `displayName`, `federationBrandName`, `tenantId`); Graph documents no query parameters here, so the lookup takes no `--select`, `--filter`, `--limit` or `--cursor`.
665
- Both lookups support only `--api-version v1.0`; `--api-version beta` fails validation before credentials, including on preview-enabled profiles.
666
- Delegated tenant-information reads default to `https://graph.microsoft.com/CrossTenantInformation.ReadBasic.All`, while application profiles use the configured `.default` audience.
667
- No Entra role is required for tenant-information lookups; personal Microsoft accounts are not supported.
668
- No P1/P2 prerequisite is stated for tenant-information lookups; denied reads name the scope and licensing guidance instead of only the generic cause.
669
- Null/missing preservation and 500-character text truncation described above also apply to tenant-information reads; `--full` removes text truncation.
670
- Lookup values bind only through the allowlisted session function-argument contract (validated, OData-quoted and encoded); every other parenthesised path keeps the refusal.
671
- No tenant-information mutation lives here; see the [tenant-lookup scope decisions](docs/coverage.md#ext-04-tenant-lookup-scope-decisions) for the three reads this contract unblocks.
10
+ mg-axi provides reviewed Entra reads and explicitly gated named writes for workforce tenants in the commercial cloud.
11
+ It emits compact TOON output, includes contextual next-step suggestions and keeps errors machine-readable on stdout.
12
+ It is read-only by default; the shipped surface does not cover all of Microsoft Graph or Entra.
13
+ The [AXI credential standard](https://github.com/knowttl/artium-axi/blob/main/docs/credential-standard.md) defines the shared profile, secret-reference and file-protection contract.
672
14
 
673
- Log in with `https://graph.microsoft.com/Domain.Read.All`, then inspect tenant domains and their DNS records:
15
+ ## Agent Ergonomics
674
16
 
675
- ```sh
676
- mg-axi login --profile soc --scopes https://graph.microsoft.com/Domain.Read.All
677
- mg-axi entra domain list --profile soc
678
- mg-axi entra domain show --profile soc --id contoso.com
679
- mg-axi entra domain verification-dns-record list --profile soc --domain contoso.com
680
- mg-axi entra domain service-configuration-record list --profile soc --domain contoso.com
681
- mg-axi entra domain-dns-record list --profile soc
682
- mg-axi entra domain-dns-record show --profile soc --id <record-id>
683
- ```
684
-
685
- `entra domain list` defaults to compact properties (`id`, `authenticationType`, `isVerified`, `isDefault`); domain ids are fully qualified names, not object UUIDs.
686
- All eight domain and DNS record commands support only `--api-version v1.0`; `--api-version beta` fails validation before credentials, including on preview-enabled profiles.
687
- Domain lists offer no `--filter`: Graph documents a known issue with `$search`, `$top` and `$filter` on domain lists, so the flag is refused before credentials.
688
- `entra domain show --id <domain-name>` defaults to the full reviewed domain set; an unverified domain points at its verification DNS records.
689
- Verification and service-configuration record lists take `--domain <domain-name>` and default to `id`, `label`, `recordType` and `supportedService`.
690
- Their show commands also take `--domain <domain-name>` and require `--id <record-id>`; all DNS record show commands default to `id`, `isOptional`, `label`, `recordType`, `supportedService` and `ttl`.
691
- DNS record lists accept `--filter` as plain `$filter`, without adding `$count=true` or `ConsistencyLevel`.
692
- Record rows carry `@odata.type` naming the derived record kind; derived-type detail (`mailExchange`, `preference`, `canonicalName`, SRV fields, `text`) needs an explicit `--select` naming the derived property.
693
- Domain and record `--select` accept their [reviewed property sets](src/entra-domains.ts); `--fields` must be a subset of the fetched selection.
694
- Domain lists return `domains` and single-domain reads return `domain`; record lists return `verificationDnsRecords`, `serviceConfigurationRecords` and `domainDnsRecords`, with single-record reads returning the singular key.
695
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to domain and DNS record reads.
696
- Resume record lists with the same `--domain` where applicable, profile, scopes and API version; omit or repeat the original server query flags, and repeat local `--fields` and `--full` when wanted.
697
- Delegated domain and DNS record reads default to `https://graph.microsoft.com/Domain.Read.All`, while application profiles use the configured `.default` audience.
698
- Delegated callers additionally need a supported Entra role (Domain Name Administrator or Global Reader are least-privileged); personal Microsoft accounts are not supported.
699
- No P1/P2 prerequisite is stated for domain reads; denied reads name the scope, role and licensing guidance instead of only the generic cause.
700
- No domain mutation lives here; see the [domain scope decisions](docs/coverage.md#ext-01-domain-scope-decisions) for deferred reads and later subfamilies.
701
-
702
- Log in with `https://graph.microsoft.com/PublicKeyInfrastructure.Read.All`, then inspect certificate-based-auth PKI configurations and their certificate authorities:
703
-
704
- ```sh
705
- mg-axi login --profile soc --scopes https://graph.microsoft.com/PublicKeyInfrastructure.Read.All
706
- mg-axi entra certificate-auth-pki list --profile soc
707
- mg-axi entra certificate-auth-pki show --profile soc --id <pki-id>
708
- mg-axi entra certificate-auth-pki count --profile soc
709
- mg-axi entra certificate-authority list --profile soc --pki <pki-id>
710
- mg-axi entra certificate-authority show --profile soc --pki <pki-id> --id <authority-id>
711
- mg-axi entra certificate-authority count --profile soc --pki <pki-id>
712
- ```
17
+ - Compact list schemas keep the first response small.
18
+ - Lists distinguish complete, empty and partial results, with server totals when available.
19
+ - Opaque cursors preserve remaining rows for bounded reads.
20
+ - `--full` removes text truncation while preserving redaction and row limits.
21
+ - Leaf-level `--help` describes required arguments, flags and examples.
22
+ - Version probes, help and the local home view require no tenant sign-in.
713
23
 
714
- `entra certificate-auth-pki list` defaults to compact properties (`id`, `displayName`, `status`); an empty list may mean certificate-based authentication is not configured.
715
- All six certificate-auth commands support only `--api-version v1.0`; `--api-version beta` fails validation before credentials, including on preview-enabled profiles.
716
- PKI and authority lists accept `--filter` as plain `$filter`, without adding `$count=true` or `ConsistencyLevel`; counts accept `--filter` to narrow the total server-side.
717
- `entra certificate-auth-pki show --id <pki-id>` defaults to the full reviewed PKI set and points at its authorities; authority lists take `--pki <pki-id>` and default to `id`, `displayName`, `certificateAuthorityType` and `expirationDateTime`.
718
- Authority show commands also take `--pki <pki-id>` and require `--id <authority-id>`; the show default is the full reviewed set except the public-certificate blob.
719
- Authority entries carry public certificates only, but the base64 `certificate` blob (up to 8 KB per CA file) is omitted from every default select and needs an explicit `--select certificate`; explicitly selected blobs still truncate at 500 characters unless `--full` is passed.
720
- PKI lists return `certificateAuthPkis` and single-PKI reads return `certificateAuthPki`; authority lists return `certificateAuthorities` with single-authority reads returning `certificateAuthority`.
721
- Count commands return `count: { returned: <total>, complete: true }` and accept no `--select`, `--fields`, `--limit` or `--cursor`.
722
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to certificate-auth reads.
723
- Delegated certificate-auth reads default to `https://graph.microsoft.com/PublicKeyInfrastructure.Read.All`, while application profiles use the configured `.default` audience.
724
- Delegated callers additionally need Privileged Authentication Administrator or Authentication Administrator; personal Microsoft accounts are not supported.
725
- No P1/P2 prerequisite is stated for certificate-auth reads; denied reads name the scope, role and licensing guidance instead of only the generic cause.
726
- No certificate-auth mutation lives here; the root `/certificateBasedAuthConfiguration` reads carry no documented v1.0 operation contract and the org-scoped certificate-auth reads belong to a later piece; see the [certificate-auth scope decisions](docs/coverage.md#ext-01-certificate-auth-scope-decisions).
727
-
728
- Log in with `https://graph.microsoft.com/Organization.Read.All`, then inspect commercial subscriptions:
24
+ ## Quick Start
729
25
 
730
- ```sh
731
- mg-axi login --profile soc --scopes https://graph.microsoft.com/Organization.Read.All
732
- mg-axi entra subscription list --profile soc
733
- mg-axi entra subscription show --profile soc --id <subscription-id>
734
- mg-axi entra subscription show --profile soc --commerce-subscription-id <commerce-subscription-id>
735
- mg-axi entra subscription count --profile soc
736
- ```
26
+ Requires Node.js **22.12.0 or later**.
27
+ Graph access also requires an organization-owned app registration, the appropriate consent and a configured profile.
737
28
 
738
- `entra subscription list` defaults to compact properties (`id`, `skuPartNumber`, `status`, `totalLicenses`); an empty list may mean the tenant holds no commercial subscriptions.
739
- All three subscription commands support only `--api-version v1.0`; `--api-version beta` fails validation before credentials, including on preview-enabled profiles.
740
- Subscription lists accept `--filter` as plain `$filter`, without adding `$count=true` or `ConsistencyLevel`; the show command documents `$select` only and the count command takes no `--filter`, `--select`, `--limit` or `--cursor`.
741
- `entra subscription show --id <subscription-id>` defaults to the full reviewed companySubscription set; `entra subscription show --commerce-subscription-id <commerce-subscription-id>` serves the same object through the commerce-system alternate key, binding it only through the allowlisted session function-argument contract (validated, OData-quoted, encoded).
742
- Subscription lists return `subscriptions` and single-subscription reads return `subscription`; count commands return `count: { returned: <total>, complete: true }`.
743
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to subscription reads.
744
- Delegated subscription reads default to `https://graph.microsoft.com/Organization.Read.All`, while application profiles use the configured `.default` audience.
745
- Delegated callers additionally need Global Reader, Directory Readers, or Dynamics 365 Business Central Administrator for read-only standard properties; personal Microsoft accounts are not supported.
746
- No P1/P2 prerequisite is stated for subscription reads; denied reads name the scope, role and licensing guidance instead of only the generic cause.
747
- No subscription mutation lives here; see the [directory-subscriptions scope decisions](docs/coverage.md#ext-01-directory-subscriptions-scope-decisions).
29
+ ### Skill installation
748
30
 
749
- Log in with `https://graph.microsoft.com/OnPremDirectorySynchronization.Read.All`, then inspect on-premises directory synchronization (delegated profiles only):
31
+ Install the agent skill:
750
32
 
751
33
  ```sh
752
- mg-axi login --profile soc --scopes https://graph.microsoft.com/OnPremDirectorySynchronization.Read.All
753
- mg-axi entra on-premises-synchronization list --profile soc
754
- mg-axi entra on-premises-synchronization show --profile soc --id <synchronization-id>
34
+ npx skills add knowttl/mg-axi --skill mg-axi
755
35
  ```
756
36
 
757
- `entra on-premises-synchronization list` and `show` default to the reviewed property set (`id`, `configuration`, `features`); an empty list may mean on-premises directory sync is not configured for the tenant.
758
- Both commands support only `--api-version v1.0`; `--api-version beta` fails validation before credentials, including on preview-enabled profiles.
759
- Neither leaf offers `--filter`: Graph documents `$select` only here, so strict input validation refuses the flag before credentials.
760
- Synchronization lists return `synchronizations` and single reads return `synchronization`; secret-shaped values inside `configuration` and `features` stay redacted by the shared session.
761
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to on-premises-synchronization reads.
762
- Delegated reads default to `https://graph.microsoft.com/OnPremDirectorySynchronization.Read.All`; application profiles are refused before credentials because Graph documents no supported application permission for this operation.
763
- Delegated callers additionally need Global Administrator, the only supported Entra role for this operation; personal Microsoft accounts are not supported.
764
- No P1/P2 prerequisite is stated for on-premises-synchronization reads; denied reads name the scope, role and licensing guidance instead of only the generic cause.
765
- No on-premises-synchronization mutation lives here; the `$count` scalar stays deferred and beta operations stay scheduled; see the [on-premises-synchronization scope decisions](docs/coverage.md#ext-01-on-premises-synchronization-scope-decisions).
37
+ The [skill](skills/mg-axi/SKILL.md) guides agents to use `npx -y @knowttl/mg-axi` and contains the full executable command table.
38
+ The skill and session hooks below are alternative discovery paths; either can be used alone.
39
+ Neither grants Graph access.
766
40
 
767
- Log in with `https://graph.microsoft.com/Agreement.Read.All`, then inspect terms-of-use agreements (delegated profiles only):
768
-
769
- ```sh
770
- mg-axi login --profile soc --scopes https://graph.microsoft.com/Agreement.Read.All
771
- mg-axi entra agreement list --profile soc
772
- mg-axi entra agreement show --profile soc --id <agreement-id>
773
- ```
41
+ ### Zero setup
774
42
 
775
- `entra agreement list` defaults to compact rows (`id`, `displayName`) and `show` defaults to the full reviewed metadata set; an empty list means no terms-of-use agreements are configured for the tenant.
776
- Agreement lists pass `--filter` through as plain `$filter`; agreement shows support `$select` only, so strict input validation refuses `--filter` before credentials.
777
- Log in with `https://graph.microsoft.com/AgreementAcceptance.Read`, then inspect acceptance records (delegated profiles only):
43
+ Inspect local status and help without installing a global binary:
778
44
 
779
45
  ```sh
780
- mg-axi login --profile soc --scopes https://graph.microsoft.com/AgreementAcceptance.Read
781
- mg-axi entra agreement acceptance list --agreement <agreement-id> --profile soc
782
- mg-axi entra agreement acceptance show --agreement <agreement-id> --id <acceptance-id> --profile soc
783
- mg-axi entra agreement-acceptance list --profile soc
784
- mg-axi entra agreement-acceptance show --id <acceptance-id> --profile soc
46
+ npx -y @knowttl/mg-axi
47
+ npx -y @knowttl/mg-axi --version
48
+ npx -y @knowttl/mg-axi entra user list --help
785
49
  ```
786
50
 
787
- Acceptance lists return `agreementAcceptances` and single reads return `agreementAcceptance`; default rows stay minimal (`id`, `agreementId`, `state`, `recordedDateTime`) because acceptance records are personal data, and identifying fields need an explicit `--select` naming them.
788
- All six commands support only `--api-version v1.0`; `--api-version beta` fails validation before credentials, including on preview-enabled profiles.
789
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to terms-of-use reads.
790
- Delegated agreement reads default to `https://graph.microsoft.com/Agreement.Read.All` and delegated acceptance reads default to `https://graph.microsoft.com/AgreementAcceptance.Read` (`AgreementAcceptance.Read.All` is the documented higher-privileged alternative); application profiles are refused before credentials because Graph documents no supported application permission for these operations.
791
- Delegated callers additionally need Security Reader, the least-privileged supported Entra role for these operations; personal Microsoft accounts are not supported.
792
- Terms of use needs Microsoft Entra ID P1; denied reads name the scope, role and licensing guidance instead of only the generic cause.
793
- Agreement file contents are never downloaded or printed; only agreement metadata is projected.
794
- No terms-of-use mutation lives here; the acceptances `$count` scalar and the agreement file/localization sub-reads stay deferred and beta operations stay scheduled; see the [terms-of-use scope decisions](docs/coverage.md#ext-01-terms-of-use-scope-decisions).
795
-
796
- Log in with `https://graph.microsoft.com/Directory.Read.All`, then inspect directory objects:
51
+ Use `npx -y @knowttl/mg-axi` in place of `mg-axi` in the examples below when running without a global install.
52
+ To pin a release, use `npx -y @knowttl/mg-axi@<version>`.
797
53
 
798
- ```sh
799
- mg-axi login --profile soc --scopes https://graph.microsoft.com/Directory.Read.All
800
- mg-axi entra directory-object list --profile soc
801
- mg-axi entra directory-object show --profile soc --id <object-id>
802
- mg-axi entra directory-object count --profile soc
803
- ```
54
+ ## Other ways to install
804
55
 
805
- `entra directory-object list` defaults to compact rows (`id` plus the `@odata.type` subtype discriminator) and `show` defaults to the reviewed base set (`id`, `deletedDateTime`) plus the discriminator; an empty list is unexpected because every tenant directory carries objects, so verify the profile tenant before treating it as empty.
806
- All three commands support only `--api-version v1.0`; `--api-version beta` fails validation before credentials, including on preview-enabled profiles.
807
- Neither list nor show offers `--filter`: Graph documents no List operation page for the collection, so strict input validation refuses the flag before credentials.
808
- Lists return `directoryObjects`, single reads return `directoryObject`, and counts return `count` with the scalar total.
809
- Rows are polymorphic: only the base-type properties are ever requested or projected (`$expand` is not offered), the discriminator rides along automatically without being selectable, and subtype secrets or credentials can never appear; subtype detail needs the subtype's named reads.
810
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to directory-object reads.
811
- Reads default to `https://graph.microsoft.com/Directory.Read.All` for delegated access, while application profiles use the configured `.default` audience.
812
- No delegated role or P1/P2 prerequisite is stated for directory-object reads; personal Microsoft accounts are not supported, and denied reads name the scope, role and licensing guidance instead of only the generic cause.
813
- No directory-object mutation lives here; the delta sync and the POST lookup/validation actions stay deferred and beta operations stay scheduled; see the [directory-objects scope decisions](docs/coverage.md#ext-01-directory-objects-scope-decisions).
56
+ ### Global install and session hooks
814
57
 
815
- Log in with the scope matching the deleted type (`User.Read.All`, `Group.Read.All`, `Application.Read.All` or `AdministrativeUnit.Read.All`), then inspect the directory recycle bin:
58
+ Install optional ambient context for Claude Code, Codex and OpenCode:
816
59
 
817
60
  ```sh
818
- mg-axi login --profile soc --scopes https://graph.microsoft.com/Group.Read.All
819
- mg-axi entra deleted-user list --profile soc
820
- mg-axi entra deleted-group list --profile soc
821
- mg-axi entra deleted-group count --profile soc
822
- mg-axi entra deleted-item show --profile soc --id <object-id> --scopes https://graph.microsoft.com/Group.Read.All
61
+ npm install --global @knowttl/mg-axi
62
+ mg-axi setup hooks
823
63
  ```
824
64
 
825
- Each deleted-type list defaults to compact rows (`id`, `displayName`, `deletedDateTime`, plus `appId` for applications and service principals) and carries the `@odata.type` discriminator naming the kind; deleted users are personal data, so user defaults stay minimal and identifying fields need an explicit `--select`.
826
- `deleted-item show --id <object-id>` reads the untyped get returning the same object for every type, so it takes no default scope: delegated callers pass the scope matching the object's type as `--scopes` (list the recycle bin first and read each row's `@odata.type` kind).
827
- All eleven deleted-item commands support only `--api-version v1.0`; `--api-version beta` fails validation before credentials, including on preview-enabled profiles.
828
- No typed list offers `--filter`: only `$select` is reviewed here, so strict input validation refuses the flag before credentials.
829
- Type lists return `deletedItems`, single reads return `deletedItem`, and counts return `count` with the scalar total.
830
- Upstream requires the OData cast as part of the list URI, so untyped list/count commands do not exist; device casts carry no documented v1.0 permission contract and stay out with an explicit deferred disposition.
831
- Rows are polymorphic: only the reviewed per-type properties are ever requested or projected, the discriminator rides along automatically without being selectable, and credential collections (`keyCredentials`, `passwordCredentials`) are never selectable, so secret values can never appear.
832
- `appId` (client ID) is distinct from the object `id` on applications and service principals.
833
- Soft-deleted security groups report `securityEnabled` false through a known upstream limitation; read `groupTypes` to name the real kind.
834
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to deleted-item reads.
835
- List and count reads default to the type's least-privileged scope for delegated access (`User.Read.All`, `Group.Read.All`, `Application.Read.All` or `AdministrativeUnit.Read.All`, all read scopes already allowlisted), while application profiles use the configured `.default` audience.
836
- No delegated role or P1/P2 prerequisite is stated for deleted-item reads; personal Microsoft accounts are not supported, and denied reads name the scope, role and licensing guidance instead of only the generic cause.
837
- No deleted-item mutation lives here; restore and permanent delete stay out entirely, and the POST lookup/validation actions stay deferred and beta operations stay scheduled; see the [deleted-items scope decisions](docs/coverage.md#ext-01-deleted-items-scope-decisions).
838
- Log in with `https://graph.microsoft.com/OrgContact.Read.All`, then inspect organizational contacts:
839
-
840
- ```sh
841
- mg-axi login --profile soc --scopes https://graph.microsoft.com/OrgContact.Read.All
842
- mg-axi entra contact list --profile soc
843
- mg-axi entra contact show --profile soc --id <contact-id>
844
- mg-axi entra contact count --profile soc
845
- mg-axi entra contact show-manager --profile soc --id <contact-id>
846
- mg-axi entra contact list-direct-reports --profile soc --id <contact-id>
847
- mg-axi entra contact show-direct-report --profile soc --id <contact-id> --report-id <report-id>
848
- mg-axi entra contact count-direct-reports --profile soc --id <contact-id>
849
- mg-axi entra contact list-member-of --profile soc --id <contact-id>
850
- mg-axi entra contact show-member-of --profile soc --id <contact-id> --member-id <membership-id>
851
- mg-axi entra contact count-member-of --profile soc --id <contact-id>
852
- ```
65
+ Restart the agent session after installation.
66
+ The session hook reports local profile names, tenant labels, authentication-cache presence, write policy and version.
67
+ It makes no network, Graph or sign-in calls and exits successfully when unconfigured.
68
+ Only the explicit hook installer changes agent integrations.
69
+ The installer rejects targets in temporary directories, linked Git worktrees and validation pipeline builds.
70
+ Install globally before running `mg-axi setup hooks`; ordinary CLI commands never auto-install hooks, including in tests and non-interactive sessions.
853
71
 
854
- Contacts are personal data: `entra contact list` and `show` default to minimal rows (`id`, `displayName`, `mail`, `companyName`), and identifying fields beyond that need an explicit `--select`.
855
- Navigation results are directory objects: `show-manager`, `list-direct-reports`, `show-direct-report`, `list-member-of` and `show-member-of` default to the `@odata.type` discriminator plus `id` and `displayName` only, with `mail` one explicit `--select` away; `--as user|orgContact` selects the typed cast route on direct-report reads and `--as group|administrativeUnit` on member-of reads, `--transitive` reads the transitiveMemberOf closure on member-of reads, and application callers with narrow consent receive limited-information rows carrying only type and id.
856
- All ten contact commands support only `--api-version v1.0`; `--api-version beta` fails validation before credentials, including on preview-enabled profiles.
857
- `entra contact list` and `list-member-of` offer `--filter`, passed through as plain `$filter` with `$count=true` and `ConsistencyLevel eventual`; `$search` and `$orderby` stay unreviewed.
858
- Direct-report reads take `$select` only; `$filter`, `$search` and `$top` stay unreviewed there.
859
- Top-level `list` returns `contacts` and `show` returns `contact`; `show-manager` returns `manager`, `list-direct-reports` returns `directReports` and `show-direct-report` returns `directReport`, `list-member-of` returns `memberOf` and `show-member-of` returns `memberOf`, and counts return `count` with the scalar total; each count sends `ConsistencyLevel eventual` like the documented `$count` example.
860
- Top-level list and show request and project only flat scalar properties (`$expand` is not offered, so navigation objects never appear there); navigation reads return directory objects through their own routes, and the nested phones/addresses collections need their own projection review.
861
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to contact reads.
862
- Reads default to `https://graph.microsoft.com/OrgContact.Read.All` for delegated access, while application profiles use the configured `.default` audience; transitive member-of reads additionally need `https://graph.microsoft.com/Group.Read.All`.
863
- Delegated callers additionally need a supported Entra role (Directory Readers reads basic properties; Global Reader, Directory Writers, Intune Administrator or User Administrator also work); personal Microsoft accounts are not supported, and denied reads name the scope, role and licensing guidance instead of only the generic cause.
864
- No contact mutation lives here; the delta sync and the POST lookup actions stay deferred and beta operations stay scheduled, and the error/sync navigation reads stay unavailable with no documented permission contract; see the [contacts scope decisions](docs/coverage.md#ext-01-contacts-scope-decisions).
72
+ ### Direct install
865
73
 
866
- Log in with `https://graph.microsoft.com/IdentityProvider.Read.All`, then read workforce identity providers:
74
+ For direct command entry, install globally and inspect setup guidance:
867
75
 
868
76
  ```sh
869
- mg-axi login --profile soc --scopes https://graph.microsoft.com/IdentityProvider.Read.All
870
- mg-axi entra identity-provider list --profile soc
871
- mg-axi entra identity-provider show --profile soc --id <provider-id>
872
- mg-axi entra identity-provider count --profile soc
873
- mg-axi entra identity-provider available-types --profile soc
77
+ npm install --global @knowttl/mg-axi
78
+ mg-axi setup
874
79
  ```
875
80
 
876
- `entra identity-provider list` defaults to compact properties (`id`, `displayName`); `show --id <provider-id>` defaults to the full reviewed provider set.
877
- All four identity-provider commands support only `--api-version v1.0`; `--api-version beta` fails validation before credentials, including on preview-enabled profiles.
878
- List and show accept `--select` from the [reviewed provider property set](src/entra-identity-providers.ts); `--fields` must be a subset of the fetched selection.
879
- Provider lists return `identityProviders`, single-provider reads return `identityProvider`, and counts return `count` with the scalar total.
880
- `available-types` returns `availableProviderTypes`, an array of type names available for the tenant, with a `count` aggregate.
881
- Available types depend on tenant configuration and licensing; availability does not mean a provider is configured.
882
- Provider lists accept `--filter` as plain `$filter`; counts accept `--filter` to narrow the total server-side.
883
- Rows carry `@odata.type` naming the provider kind (social or built-in).
884
- The reviewed workforce fields are `id`, `displayName`, `identityProviderType`, and `clientId`.
885
- `clientSecret` and `certificateData` are never selectable and any row carrying them is scrubbed before output, so key material can never reach stdout, errors or logs.
886
- Provider lists use the named-list caps, uniform `count` totals and cursors described above.
887
- List and show preserve null/missing properties and truncate text longer than 500 characters; `--full` restores complete text without lifting redaction or row caps.
888
- Delegated provider reads default to `https://graph.microsoft.com/IdentityProvider.Read.All`, while application profiles use the configured `.default` audience.
889
- Delegated callers additionally need a directory role that can read federation configuration (Global Reader is the least-privileged read-only directory role); personal Microsoft accounts are not supported.
890
- No per-operation licence prerequisite is stated for these reads; denied reads name the scope, role and licensing guidance instead of only the generic cause.
891
- Workforce tenants only; external-customer (B2C/External ID) user flows, cross-tenant access and provisioning are separate later pieces (see the [identity-provider scope decisions](docs/coverage.md#ext-03-identity-provider-scope-decisions)).
892
-
893
- Log in with `https://graph.microsoft.com/Domain.Read.All`, then read workforce directory federation configurations:
81
+ `setup` displays installation, configuration and capability guidance.
82
+ It writes nothing, signs in nowhere and installs no hooks or skills.
894
83
 
895
- ```sh
896
- mg-axi login --profile soc --scopes https://graph.microsoft.com/Domain.Read.All
897
- mg-axi entra federation-configuration list --profile soc
898
- mg-axi entra federation-configuration show --profile soc --id <configuration-id>
899
- mg-axi entra federation-configuration count --profile soc
900
- mg-axi entra federation-configuration available-types --profile soc
901
- ```
84
+ ## Usage
902
85
 
903
- `entra federation-configuration list` defaults to compact properties (`id`, `displayName`); `show --id <configuration-id>` defaults to the full reviewed set except the signing-certificate blob, which needs an explicit `--select signingCertificate`.
904
- All four federation-configuration commands support only `--api-version v1.0`; `--api-version beta` fails validation before credentials, including on preview-enabled profiles.
905
- List and show accept `--select` from the [reviewed configuration property set](src/entra-federation-configurations.ts); `--fields` must be a subset of the fetched selection.
906
- Configuration lists return `federationConfigurations`, single-configuration reads return `federationConfiguration`, and counts return `count` with the scalar total.
907
- `available-types` returns `availableProviderTypes`, an array of type names available for the tenant, with a `count` aggregate; it defaults to `https://graph.microsoft.com/IdentityProvider.Read.All` while list, show and count default to `https://graph.microsoft.com/Domain.Read.All`, and application profiles use the configured `.default` audience.
908
- `signingCertificate` carries the public token-signing key only and is omitted from every default select; request it explicitly when rotation evidence is needed. No private key material exists on these resources.
909
- Configuration lists accept `--filter` as plain `$filter`; counts accept `--filter` to narrow the total server-side.
910
- Rows carry `@odata.type` naming the configuration kind.
911
- Configuration lists use the named-list caps, uniform `count` totals and cursors described above.
912
- List and show preserve null/missing properties and truncate text longer than 500 characters; `--full` restores complete text without lifting row caps.
913
- Delegated callers additionally need External Identity Provider Administrator, the least-privileged supported Entra role for these reads; personal Microsoft accounts are not supported.
914
- No per-operation licence prerequisite is stated for these reads; denied reads name the scope, role and licensing guidance instead of only the generic cause.
915
- No domain federationConfiguration sub-read, no domains navigation expansion and no mutation live here; beta operations stay scheduled (see the [federation-configuration scope decisions](docs/coverage.md#ext-01-federation-configuration-scope-decisions)).
916
-
917
- Log in with `https://graph.microsoft.com/User.Export.All,https://graph.microsoft.com/User.Read.All`, then read workforce data-policy operations:
86
+ Create a delegated profile using the tenant and app registration UUIDs, then explicitly sign in for user reads:
918
87
 
919
88
  ```sh
920
- mg-axi login --profile soc --scopes https://graph.microsoft.com/User.Export.All,https://graph.microsoft.com/User.Read.All
921
- mg-axi entra data-policy-operation list --profile soc
922
- mg-axi entra data-policy-operation show --profile soc --id <operation-id>
923
- mg-axi entra data-policy-operation count --profile soc
89
+ mg-axi profile create --name soc --tenant <tenant-id> --client <client-id> --cloud commercial
90
+ mg-axi auth login --profile soc --scopes https://graph.microsoft.com/User.Read.All
91
+ mg-axi doctor --profile soc
92
+ mg-axi entra user list --profile soc --limit 10
93
+ mg-axi entra user show --profile soc --id <user-id-or-upn>
924
94
  ```
925
95
 
926
- `entra data-policy-operation list` defaults to compact properties (`id`, `status`, `userId`, `submittedDateTime`); `show --id <operation-id>` defaults to the full reviewed operation set.
927
- All three data-policy-operation commands support only `--api-version v1.0`; `--api-version beta` fails validation before credentials, including on preview-enabled profiles.
928
- List and show accept `--select` from the [reviewed operation property set](src/entra-data-policy-operations.ts); `--fields` must be a subset of the fetched selection.
929
- The list documents `$select` only, so no `--filter`; the `$count` route takes no `--filter`, `--select`, `--limit` or `--cursor`.
930
- Operation lists return `dataPolicyOperations`, single-operation reads return `dataPolicyOperation`, and counts return `count` with the scalar total.
931
- `storageLocation` always renders as `***redacted***`: export blob URLs and signed links never reach output, errors or logs.
932
- Operation lists use the named-list caps, uniform `count` totals and cursors described above.
933
- List and show preserve null/missing properties and truncate text longer than 500 characters; `--full` restores complete text without lifting redaction or row caps.
934
- Delegated operation reads default to `https://graph.microsoft.com/User.Export.All,https://graph.microsoft.com/User.Read.All`, while application profiles use the configured `.default` audience.
935
- Delegated callers additionally need Company Administrator, the privileged role documented for export reads; personal Microsoft accounts are not supported.
936
- No P1/P2 prerequisite is stated for these reads; denied reads name the scopes, role and licensing guidance instead of only the generic cause.
937
- No export submission lives here; the `$count` scalar aside, beta operations stay scheduled (see the [data-policy-operations scope decisions](docs/coverage.md#ext-03-data-policy-operations-scope-decisions)).
938
-
939
- Log in with `https://graph.microsoft.com/RiskPreventionProviders.Read.All`, then read workforce risk-prevention providers and verifications (delegated profiles only):
940
-
941
- ```sh
942
- mg-axi login --profile soc --scopes https://graph.microsoft.com/RiskPreventionProviders.Read.All
943
- mg-axi entra fraud-protection-provider list --profile soc
944
- mg-axi entra fraud-protection-provider show --profile soc --id <provider-id>
945
- mg-axi entra fraud-protection-provider count --profile soc
946
- mg-axi entra web-application-firewall-provider list --profile soc
947
- mg-axi entra web-application-firewall-provider show --profile soc --id <provider-id>
948
- mg-axi entra web-application-firewall-provider count --profile soc
949
- mg-axi entra web-application-firewall-verification list --profile soc
950
- mg-axi entra web-application-firewall-verification show --profile soc --id <verification-id>
951
- mg-axi entra web-application-firewall-verification count --profile soc
952
- mg-axi entra web-application-firewall-verification provider show --profile soc --id <verification-id>
953
- ```
96
+ The first profile created becomes the default; `--profile` selects another identity, followed by `MG_AXI_PROFILE`, the default and the only-profile fallback.
97
+ Profile creation does not overwrite existing identities or sign in.
98
+ `doctor` performs one bounded user-list read per selected profile with silent credential acquisition.
99
+ Without `--profile`, it checks the default profile, or all profiles if no default is set.
100
+ Missing configuration and failed reads return errors rather than suggesting that access succeeded.
954
101
 
955
- `entra fraud-protection-provider list` and `entra web-application-firewall-provider list` default to compact properties (`id`, `displayName`); `entra web-application-firewall-verification list` defaults to (`id`, `verifiedHost`, `providerType`); every `show` defaults to its full reviewed set.
956
- All ten risk-prevention commands support only `--api-version v1.0`; `--api-version beta` fails validation before credentials, including on preview-enabled profiles.
957
- List and show accept `--select` from the [reviewed risk-prevention property sets](src/entra-risk-prevention.ts); `--fields` must be a subset of the fetched selection.
958
- The fraud list documents `$select` only, so no `--filter`; the WAF and verification lists accept `--filter` as plain `$filter`; every `$count` route takes no `--filter`, `--select`, `--limit` or `--cursor`.
959
- Fraud lists return `fraudProtectionProviders`, WAF lists return `webApplicationFirewallProviders`, verification lists return `webApplicationFirewallVerifications`; single reads return `fraudProtectionProvider`, `webApplicationFirewallProvider` or `webApplicationFirewallVerification`; counts return `count` with the scalar total.
960
- Verification rows never carry provider detail; `provider show --id <verification-id>` returns the owning `webApplicationFirewallProvider`.
961
- Fraud and WAF rows carry `@odata.type` naming the provider kind (Arkose or HUMAN; Akamai or Cloudflare).
962
- The reviewed fraud fields are `id`, `displayName`, `clientSubDomain`, `verifySubDomain` and `appId`; the reviewed WAF fields are `id`, `displayName`, `hostPrefix` and `zoneId`; the reviewed verification fields are `id`, `verifiedHost`, `providerType`, `verificationResult` and `verifiedDetails`.
963
- Arkose `privateKey`/`publicKey`, HUMAN `serverToken`, Akamai `clientSecret`/`clientToken`/`accessToken` and Cloudflare `apiToken` are never selectable and any row carrying them is scrubbed before output, so key material can never reach stdout, errors or logs.
964
- Risk-prevention lists use the named-list caps, uniform `count` totals and cursors described above.
965
- List and show preserve null/missing properties and truncate text longer than 500 characters; `--full` restores complete text without lifting redaction or row caps.
966
- Delegated risk-prevention reads default to `https://graph.microsoft.com/RiskPreventionProviders.Read.All`; Graph documents no supported application permission, so application profiles are refused before credentials.
967
- Delegated callers additionally need a directory role that can read risk-prevention configuration (Security Reader is the least-privileged read-only role); personal Microsoft accounts are not supported.
968
- No per-operation licence prerequisite is stated for these reads; denied reads name the scope, role and licensing guidance instead of only the generic cause.
969
- No provider create/update/delete and no verification submission live here; beta operations stay scheduled (see the [risk-prevention scope decisions](docs/coverage.md#ext-03-risk-prevention-scope-decisions)).
970
-
971
- Log in with `https://graph.microsoft.com/Policy.Read.All`, then read Conditional Access policies, named locations and templates as separate grammar:
102
+ Browser login is the default and uses MSAL with PKCE.
103
+ Use full Graph scope names separated by commas; delegated login rejects `.default`.
104
+ Device code requires both profile creation with `--allow-device-code` and login with `--method device-code`, subject to organization policy.
105
+ Browser failure never falls back to device code.
106
+ Ordinary reads acquire credentials silently and report when login, consent or policy intervention is required.
107
+ Login does not modify app registration or automatically request blanket write consent.
108
+
109
+ ### Command surface and subcommands
110
+
111
+ The [capability report](docs/coverage.md) distinguishes implemented operations from deferred or excluded ones.
112
+ The [command table](skills/mg-axi/SKILL.md#orientation) lists every executable leaf; use its leaf-level help for exact flags and permission defaults.
113
+
114
+ | Area | Shipped surface |
115
+ | --- | --- |
116
+ | Local configuration | `profile create`, `profile list`, `profile show`, `config path`, `auth login`, `auth logout`, `auth status`, `home`, `setup`, `setup hooks`, `doctor` |
117
+ | Directory | Users, groups and memberships, devices, administrative units, directory objects, deleted items, contacts, organizations and branding |
118
+ | Applications and consent | Applications, service principals, owners, federated credentials, delegated grants and app-role assignments |
119
+ | Roles and governance | Directory roles and assignments, PIM reads, access reviews, entitlement management and lifecycle workflows |
120
+ | Security | Conditional Access, authentication methods and registration reports, audit/sign-in/provisioning logs, user and workload risk |
121
+ | Tenant configuration | Domains and DNS records, subscriptions, certificate authentication, synchronization, terms of use, custom security attributes and group lifecycle policies |
122
+ | Identity and partner configuration | Identity providers, federation configurations, risk-prevention providers, data policy operations, contracts, delegated administration, multitenant organization and tenant information |
123
+ | Reviewed raw reads | `api get <path>` for allowlisted v1.0 routes |
124
+ | Named writes | Group member add, account enable/disable, session revocation, Conditional Access policy update and risky-user dismissal |
125
+
126
+ Delegated read commands default to the scope for their operation, which may differ from the user-read scope in the quick start.
127
+ Sign in explicitly with the appropriate scopes before switching areas.
128
+ Read commands reject unapproved or write scopes before acquiring credentials.
129
+ Permissions, directory roles and licences vary by operation; see the [access and licence guidance](docs/graph-coverage.md).
130
+ A 403 response alone does not identify which prerequisite is missing.
131
+
132
+ ```sh
133
+ # Group membership reads
134
+ mg-axi auth login --profile soc --scopes https://graph.microsoft.com/GroupMember.Read.All
135
+ mg-axi entra group list --profile soc --limit 10
136
+ mg-axi entra group member list --profile soc --group <group-id>
972
137
 
973
- ```sh
974
- mg-axi login --profile soc --scopes https://graph.microsoft.com/Policy.Read.All
975
- mg-axi entra conditional-access policy list --profile soc --limit 10
976
- mg-axi entra conditional-access policy list --profile soc --filter "state eq 'enabled'"
138
+ # Conditional Access inspection
139
+ mg-axi auth login --profile soc --scopes https://graph.microsoft.com/Policy.Read.All
140
+ mg-axi entra conditional-access policy list --profile soc
977
141
  mg-axi entra conditional-access policy show --profile soc --id <policy-id>
978
- mg-axi entra conditional-access named-location list --profile soc
979
- mg-axi entra conditional-access named-location show --profile soc --id <named-location-id>
980
- mg-axi entra conditional-access template list --profile soc --limit 10
981
- mg-axi entra conditional-access template show --profile soc --id <template-id>
982
- mg-axi entra conditional-access deleted-policy list --profile soc --limit 10
983
- mg-axi entra conditional-access deleted-policy show --profile soc --id <deleted-policy-id>
984
- mg-axi entra conditional-access deleted-named-location list --profile soc
985
- mg-axi entra conditional-access deleted-named-location show --profile soc --id <deleted-named-location-id>
986
- ```
987
-
988
- Log in with `https://graph.microsoft.com/AuthenticationContext.Read.All`, then read authentication context class references through the same session:
989
142
 
990
- ```sh
991
- mg-axi login --profile soc --scopes https://graph.microsoft.com/AuthenticationContext.Read.All
992
- mg-axi entra conditional-access auth-context list --profile soc --limit 10
993
- mg-axi entra conditional-access auth-context show --profile soc --id <auth-context-id>
143
+ # Reviewed raw reads
144
+ mg-axi auth login --profile soc --scopes https://graph.microsoft.com/User.Read.All,https://graph.microsoft.com/GroupMember.Read.All
145
+ mg-axi api get /users --profile soc --scopes https://graph.microsoft.com/User.Read.All
146
+ mg-axi api get /groups --profile soc --odata '$filter=securityEnabled eq true&$top=5' --scopes https://graph.microsoft.com/GroupMember.Read.All
994
147
  ```
995
148
 
996
- Log in with `https://graph.microsoft.com/Policy.Read.AuthenticationMethod`, then read the authentication-strength family through the same session:
149
+ Raw access is not an unrestricted Graph client.
150
+ Unreviewed routes, secret-returning routes, mail/file content, beta raw reads and raw writes are refused before credentials.
151
+ Returned data is restricted to reviewed fields; completeness describes the requested collection, not full Graph property coverage.
152
+ Secret material, authentication-method phone numbers and data-policy storage locations are redacted, including in full output, previews and audit metadata.
997
153
 
998
- ```sh
999
- mg-axi login --profile soc --scopes https://graph.microsoft.com/Policy.Read.AuthenticationMethod
1000
- mg-axi entra conditional-access auth-strength-policy list --profile soc --limit 10
1001
- mg-axi entra conditional-access auth-strength-policy show --profile soc --id <auth-strength-policy-id>
1002
- mg-axi entra conditional-access combination-configuration list --policy <auth-strength-policy-id> --profile soc
1003
- mg-axi entra conditional-access combination-configuration show --policy <auth-strength-policy-id> --id <combination-configuration-id> --profile soc
1004
- mg-axi entra conditional-access auth-method-mode list --profile soc --limit 10
1005
- mg-axi entra conditional-access auth-method-mode show --profile soc --id <auth-method-mode-id>
1006
- ```
154
+ ### TOON output behavior
1007
155
 
1008
- `entra conditional-access policy list` defaults to compact properties (`id`, `displayName`, `state`); `policy show --id <policy-id>` defaults to the full reviewed condition and control property set.
1009
- `entra conditional-access named-location list` defaults to `id` and `displayName`; `named-location show --id <named-location-id>` defaults to the full reviewed location set.
1010
- `entra conditional-access auth-strength-policy list` defaults to compact properties (`id`, `displayName`, `policyType`); `auth-strength-policy show --id <auth-strength-policy-id>` defaults to the full reviewed strength set.
1011
- `entra conditional-access combination-configuration list --policy <auth-strength-policy-id>` defaults to `id`; `combination-configuration show` takes `--policy` plus `--id` and defaults to the full reviewed base set.
1012
- `entra conditional-access auth-method-mode list` defaults to `id` and `displayName`; `auth-method-mode show --id <auth-method-mode-id>` defaults to the full reviewed mode set.
1013
- `entra conditional-access template list` defaults to `id` and `name`; `template show --id <template-id>` defaults to the full reviewed template set.
1014
- `entra conditional-access auth-context list` defaults to `id` and `displayName`; `auth-context show --id <auth-context-id>` defaults to the full reviewed authentication-context set.
1015
- `entra conditional-access deleted-policy list` defaults to compact properties (`id`, `displayName`, `state`, `deletedDateTime`); `deleted-policy show --id <deleted-policy-id>` defaults to the full reviewed deleted-policy set. Deleted-policy reads never restore; restore is a beta-only mutation outside mg-axi scope.
1016
- `entra conditional-access deleted-named-location list` defaults to `id`, `displayName` and `deletedDateTime`; `deleted-named-location show --id <deleted-named-location-id>` defaults to the full reviewed deleted-location set. Deleted named-location reads never restore; restore is a beta-only mutation outside mg-axi scope.
1017
- Policy `--select` accepts the [reviewed policy property set](src/entra-conditional-access.ts) and location `--select` accepts the [reviewed location property set](src/entra-conditional-access.ts); strength, combination-configuration, method-mode, template, authentication-context, deleted-policy and deleted-named-location `--select` accept their own [reviewed property sets](src/entra-conditional-access.ts); `--fields` must be a subset of the fetched selection in each family.
1018
- Returned `@odata.type` stays visible on named-location rows so IP and country locations stay distinguishable alongside any `--fields` projection.
1019
- Returned `@odata.type` also stays visible on combination-configuration rows so configuration kinds stay distinguishable alongside any `--fields` projection.
1020
- Returned `@odata.type` also stays visible on deleted-named-location rows so IP and country locations stay distinguishable alongside any `--fields` projection.
1021
- Policy lists return `policies`, location lists return `namedLocations`, single-policy reads return `policy` and single-location reads return `namedLocation`.
1022
- Strength lists return `authStrengthPolicies`, combination lists return `combinationConfigurations`, mode lists return `authMethodModes`, template lists return `templates`, authentication-context lists return `authContexts`, deleted-policy lists return `deletedPolicies` and deleted-named-location lists return `deletedNamedLocations`; single reads return `authStrengthPolicy`, `combinationConfiguration`, `authMethodMode`, `template`, `authContext`, `deletedPolicy` and `deletedNamedLocation`.
1023
- Successful empty collections return an empty list with a uniform `count` line (`0 policies` or `0 named locations`, `total: null`) and absence guidance.
1024
- The authentication-strength, combination-configuration, authentication-method-mode, template, authentication-context, deleted-policy and deleted-named-location lists report uniform totals through the shared list-totals helper beside a `count` line such as `2 auth-strength policies` or `2 templates` (`0 ...` with absence guidance on empty results).
1025
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply to Conditional Access reads, including nested condition values; every truncated value carries a `--full` hint.
1026
- Resume any Conditional Access list with `--cursor -` and supply the returned cursor on stdin, for example `mg-axi entra conditional-access policy list --profile soc --cursor - < cursor.txt`.
1027
- Resume combination-configuration lists with the same `--policy`, profile, scopes and API version.
1028
- Small cursors can also use `--cursor <token>`; both forms enforce a 16 MB size ceiling.
1029
- Use `--full` when reasoning from complete condition or control text; `--select` and `--fields` still determine which properties are visible, and missing properties remain unknown.
1030
- To replay a resumed result with `--full`, reuse the original input cursor; the returned cursor continues after that result.
1031
- Delegated policy, named-location, template, deleted-policy and deleted-named-location reads default to `https://graph.microsoft.com/Policy.Read.All`; delegated authentication-strength-policy, combination-configuration and authentication-method-mode reads default to `https://graph.microsoft.com/Policy.Read.AuthenticationMethod`; delegated authentication-context reads default to `https://graph.microsoft.com/AuthenticationContext.Read.All`; application profiles require the admin-consented scope for the family, use the configured Graph `.default` audience and reject `--scopes`.
1032
- HTTP 403 policy, location, template, deleted-policy and deleted-named-location errors name the operation's supported directory roles (Conditional Access Administrator, Global Reader, Global Secure Access Administrator, Security Administrator or Security Reader for delegated access); HTTP 403 authentication-context errors name the same broad role set with `AuthenticationContext.Read.All`; HTTP 403 authentication-strength errors name the narrower role set (Conditional Access Administrator, Security Administrator or Security Reader) with `Policy.Read.AuthenticationMethod`; all include guidance from the [licensing contract](docs/graph-coverage.md#licence-matrix-by-area), and Conditional Access reads need P1; the response alone does not identify the missing prerequisite.
1033
- WRITE-04 adds a named write: `mg-axi entra conditional-access policy update --id <policy-id>` PATCHes one policy with only the reviewed fields `--display-name`, `--state`, `--conditions`, `--grant-controls` and `--session-controls` (JSON objects for the last three); any other property has no flag and is refused before credentials.
1034
- At least one reviewed field flag is required.
1035
- The command supports only `--api-version v1.0`; `--state` accepts `enabled`, `enabledForReportingButNotEnforced` or `disabled`, and each JSON flag supplies the full corresponding block.
1036
- Without `--execute` the command previews the redacted current-versus-proposed diff read through the policy show route and journals nothing; an already-desired value set is a no-op with exit 0 only when all requested values can be verified equal, with redacted current values treated as unknown.
1037
- Every `--execute` run is disruptive and needs `--confirm '<policy-id>'` repeating the target exactly.
1038
- The preview carries a lockout analysis over the proposed effective policy; changes to `state`, `conditions`, `grantControls` or `sessionControls` enforce its gates before sending and on fresh reads.
1039
- An enabled all-users policy under block controls is refused outright, even with `--acknowledge-lockout-risk`, when it covers browser and modern clients on all cloud apps or `MicrosoftAdminPortals` without exclusions of admin portals or other statically decidable narrowing conditions.
1040
- Application filters cannot be evaluated statically and never narrow coverage; user, group and role exclusions also cannot establish emergency-access protection without verified principal coverage, so they do not exempt an otherwise full lockout from refusal.
1041
- Unrelated application exclusions preserve administrative coverage; universal platform (`all`) and location (`All`) includes with empty exclusions also preserve full coverage and hard refusal.
1042
- Only explicitly recognized conditions reduce coverage; protocol annotations, unknown properties and null values do not narrow it.
1043
- Non-empty `signInRiskLevels` or `userRiskLevels` containing only `low`, `medium` or `high` narrow coverage to risky sign-ins or users.
1044
- Policies whose client types do not cover both browser and modern clients, whose application inclusions cover neither `All` nor `MicrosoftAdminPortals`, or whose application exclusions contain `All` or `MicrosoftAdminPortals` show a scoped warning and require acknowledgement rather than being classified as full lockouts.
1045
- Non-universal platform or location includes, non-empty platform or location exclusions, and the risk-level conditions described above also produce scoped warnings.
1046
- Every other enabled policy needs `--acknowledge-lockout-risk` after confirming emergency-access coverage: explicit user, group or role targets and exclusions alone cannot establish that admins and break-glass accounts are protected.
1047
- Enforcement changes stay disabled when the effective state, conditions, user scope, exclusions, client types, application inclusions or exclusions, platform or location inclusions or exclusions, or grant controls are unreadable; omitted `excludeUsers`, `excludeGroups`, `clientAppTypes`, `applications.includeApplications` or `applications.excludeApplications` makes the analysis unavailable.
1048
- Disabled and report-only effective policies do not enforce access; display-name-only changes need no lockout acknowledgement or available analysis.
1049
- Graph documents no ETag or If-Match precondition for this endpoint, so the command sends none and promises no concurrency protection.
1050
- After execution, verify the policy with `mg-axi entra conditional-access policy show --id <policy-id> --profile soc`; the successful PATCH returns no policy body, and an unknown outcome requires readback before further action, never a blind replay.
1051
- The mutation needs D/A `Policy.Read.All` plus `Policy.ReadWrite.ConditionalAccess`; delegated callers additionally need Conditional Access Administrator or Security Administrator, and CA needs P1 (P2 for risk-based features).
1052
-
1053
- Inspect one user's authentication methods and the tenant registration report; delegated profiles first need explicit login with the read scopes:
156
+ | Control | Behavior |
157
+ | --- | --- |
158
+ | `--select` | Request reviewed properties where supported; some routes select locally instead |
159
+ | `--fields` | Project locally from properties already fetched |
160
+ | `--full` | Remove 500-character text truncation without lifting redaction or row caps |
161
+ | `--limit` | Cap collection rows, normally 100 by default |
162
+ | `--all` | Follow pages within request, byte and deadline budgets; incompatible with `--limit` |
163
+ | `--cursor` | Resume a partial collection under the same profile, scopes and API version |
1054
164
 
1055
- ```sh
1056
- mg-axi login --profile soc --scopes https://graph.microsoft.com/UserAuthenticationMethod.Read.All,https://graph.microsoft.com/AuditLog.Read.All
1057
- mg-axi entra user authentication-method list --profile soc --user <user-id>
1058
- mg-axi entra user authentication-method list --profile soc --user <user-id> --select id,displayName,phoneType
1059
- mg-axi entra registration list --profile soc
1060
- mg-axi entra registration list --profile soc --filter "isMfaRegistered eq false"
1061
- ```
165
+ Availability varies by leaf; consult its `--help`.
166
+ List output includes `count`, `total` and `complete` information.
167
+ For example, a partial user list without a server total reports `count: 10 users shown, more available` and `total: null`.
168
+ A known server total uses a count such as `10 of 143 users`; totals are never invented or fetched with an extra request.
169
+
170
+ Partial results preserve buffered rows and the next page in an opaque cursor.
171
+ For named reads, repeat the original query flags or omit them; conflicting queries are rejected.
172
+ Repeat `--fields` and `--full` to retain the same local view.
173
+ Raw reads reuse their original query when `--odata` is omitted on resume.
174
+ For large raw cursors, save the token to a file and supply it through stdin:
175
+
176
+ ```sh
177
+ mg-axi api get /users --profile soc --cursor - --all --scopes https://graph.microsoft.com/User.Read.All < cursor.txt
178
+ ```
179
+
180
+ Both raw cursor input forms have a 16 MB ceiling.
181
+ Treat cursor tokens and output as tenant data.
182
+ Denied reads return structured errors, not empty success results.
183
+
184
+ ### Named writes and safety
185
+
186
+ **Writes require manual profile opt-in, an operation allowlist, `--execute` and exact target confirmation.**
187
+ Without `--execute`, a named write previews the change and journals nothing, but preview still requires the write-enabled profile and prerequisite read access.
188
+ `MG_AXI_READ_ONLY=1` overrides profile opt-in.
189
+ Raw writes and beta writes remain blocked.
190
+
191
+ Manually add a `writes` object to the intended profile in the configuration file:
192
+
193
+ ```json
194
+ "writes": {
195
+ "allowWrites": true,
196
+ "operations": ["mg.entra.group.member.add"]
197
+ }
198
+ ```
199
+
200
+ No CLI command enables this policy.
201
+ The operation names are exact and scoped to the configured tenant:
202
+
203
+ | Command | Allowlist operation | `--confirm` repeats |
204
+ | --- | --- | --- |
205
+ | `entra group member add` | `mg.entra.group.member.add` | `--group` |
206
+ | `entra user update` | `entra.user.update` | `--user` |
207
+ | `entra user revoke-sessions` | `entra.user.revokeSessions` | `--user` |
208
+ | `entra conditional-access policy update` | `entra.conditional-access.policy.update` | `--id` |
209
+ | `entra risky-user dismiss` | `entra.risky-user.dismiss` | `--user` |
210
+
211
+ These examples preview only after the corresponding policy and permissions are configured:
212
+
213
+ ```sh
214
+ mg-axi entra group member add --profile soc --group <group-id> --user <user-id>
215
+ mg-axi entra user update --profile soc --user <user-id-or-upn> --account-enabled false
216
+ mg-axi entra user revoke-sessions --profile soc --user <user-id>
217
+ mg-axi entra conditional-access policy update --profile soc --id <policy-id> --state disabled
218
+ mg-axi entra risky-user dismiss --profile soc --user <risky-user-id>
219
+ ```
220
+
221
+ Adding `--execute --confirm <exact-target>` authorizes sending.
222
+ Review the preview and operation-specific permissions first:
223
+
224
+ | Write | Permission and safety boundary |
225
+ | --- | --- |
226
+ | Group member add | `GroupMember.ReadWrite.All`, plus user-read permission; delegated callers need a supported groups role. Only user object IDs are accepted. Role-assignable, dynamic and distribution groups are refused. Existing membership is a no-op. |
227
+ | Account enable/disable | `User.EnableDisableAccount.All` plus `User.Read.All`. Delegated callers need Privileged Authentication Administrator for admin targets and must generally outrank the target; application callers also need a higher-privileged admin role assignment. Both enabling and disabling are disruptive. `--account-enabled` accepts exactly `true` or `false`. IDs and literal UPNs are accepted; quote guest UPNs containing `#EXT#`. |
228
+ | Session revocation | `User.RevokeSessions.All` plus prerequisite user-read access. Only user object IDs are accepted. The action cannot be undone, may take minutes to affect sessions and does not revoke external users' home-tenant sessions. Accepted 2xx means the action was accepted, without verification readback. |
229
+ | Conditional Access update | `Policy.Read.All` plus `Policy.ReadWrite.ConditionalAccess`; delegated callers need Conditional Access Administrator or Security Administrator. P1 is required, with P2 for risk-based features. Reviewed fields only; JSON flags replace their entire corresponding block. |
230
+ | Risk dismissal | `IdentityRiskyUser.ReadWrite.All` plus `IdentityRiskyUser.Read.All`; delegated callers need Security Administrator, and risky-user access requires P2. Dismissal is not remediation: it resets no credential and revokes no session. |
231
+
232
+ Conditional Access enforcement changes require a readable lockout analysis.
233
+ Enabled policies require `--acknowledge-lockout-risk` after verifying emergency-access coverage.
234
+ An all-users block policy covering browser and modern clients on all apps or admin portals without recognized narrowing conditions is refused even with acknowledgement.
235
+ User, group and role exclusions or application filters alone cannot establish emergency-access protection.
236
+ Disabled and report-only policies do not enforce access; display-name-only changes do not require lockout acknowledgement.
237
+ See the [policy implementation](src/entra-ca-policy-update.ts) for the precise static-analysis boundary.
238
+
239
+ Execution journals intent durably before sending and records its outcome.
240
+ A failed intent record blocks the send; journal locks and incomplete records require manual reconciliation.
241
+ Writes are not automatically retried or replayed.
242
+ After `OUTCOME_UNKNOWN` or an outcome-recording failure, read back the target before further action and never replay that intent.
243
+ Account updates and risk dismissal verify the resulting state; a mismatch reports `WRITE_CONFLICT` and a failed verification read reports `OUTCOME_UNKNOWN`.
244
+ After a Conditional Access update, explicitly read the policy back.
245
+ Fresh reads do not make writes atomic, and account and policy updates send no ETag precondition or promise of concurrency protection.
246
+ Do not assume rollback.
247
+ The [execution contract](docs/execution.md#named-writes) describes the shared guarantees.
248
+
249
+ ## Configuration
250
+
251
+ Profiles store tenant/client/cloud identity, authentication mode, policy and credential references, never secret values.
252
+ Only commercial-cloud workforce profiles are supported.
253
+ Unknown configuration fields, unsupported versions/clouds and inline credentials are rejected.
254
+
255
+ | Variable | Meaning |
256
+ | --- | --- |
257
+ | `MG_AXI_CONFIG` | Configuration file override; `--config` takes precedence |
258
+ | `MG_AXI_PROFILE` | Profile selection override; `--profile` takes precedence |
259
+ | `MG_AXI_READ_ONLY` | `1` or `true` refuses named writes; `0`, `false` or unset allows profile policy; other values fail |
260
+ | `MG_AXI_WRITE_LOG` | Nonblank mutation journal path override; defaults to the state directory's `writes.log` |
261
+
262
+ On Linux and macOS, configuration lives at `$XDG_CONFIG_HOME/mg-axi/config.json` (default `~/.config/mg-axi/config.json`) and state at `$XDG_STATE_HOME/mg-axi/` (default `~/.local/state/mg-axi/`).
263
+ Only absolute XDG directory overrides are used.
264
+ On Windows, XDG variables are ignored; configuration uses `%APPDATA%\mg-axi\config.json` and state uses `%LOCALAPPDATA%\mg-axi\`.
265
+ Run `mg-axi config path` to see the selected configuration path.
266
+ Legacy `~/.mg-axi/config.json` is never read; when it exists and the selected new config does not, `CONFIG_MOVED` reports both paths and the exact move command.
267
+
268
+ Profile selection is `--profile`, `MG_AXI_PROFILE`, a complete env profile, `defaultProfile`, then the only configured profile.
269
+ An env profile requires `MG_AXI_MODE`, `MG_AXI_TENANT_ID`, `MG_AXI_CLIENT_ID` and its credential fields, plus `MG_AXI_AUTH` for application mode.
270
+ For example, client-secret auth requires `MG_AXI_CLIENT_SECRET`; certificate auth requires `MG_AXI_PRIVATE_KEY` and `MG_AXI_THUMBPRINT_SHA256`; federation requires `MG_AXI_TOKEN` containing the assertion.
271
+ Delegated env profiles require `MG_AXI_TOKEN` containing a serialized MSAL cache.
272
+ Optional `MG_AXI_ENABLED_PACKS`, `MG_AXI_SENSITIVE_AREAS` and `MG_AXI_WRITES` use JSON values; `MG_AXI_PREVIEW` and `MG_AXI_ALLOW_DEVICE_CODE` use the same boolean forms as `MG_AXI_READ_ONLY`.
273
+ A partial env profile fails with `CONFIG_INVALID` instead of falling through to a file profile.
274
+ Explicitly selected file profiles ignore the env profile fields and resolve only their own SecretRefs.
1062
275
 
1063
- `entra user authentication-method list` targets one named user (`--user` takes the user object ID or UPN) and defaults to `id`, `displayName` and `createdDateTime`; rows carry `@odata.type` naming the method kind.
1064
- There is no tenant scan through per-user methods: aggregate MFA coverage belongs to `entra registration list`, and the method output points there.
1065
- `entra registration list` defaults to `id`, `userPrincipalName`, `userDisplayName` and `isMfaRegistered` and returns the tenant MFA/SSPR posture.
1066
- Method lists return `authenticationMethods`; registration reports return `registrationDetails`.
1067
- The report does not cover disabled users, so absence from it is never proof of no MFA; that gap rides in the leaf help and every report output.
1068
- Phone numbers are protected values: the shared session replaces them with the redaction marker before output or cursor buffering (including resume cursors), so neither output nor cursors ever carry one; `--full` never lifts that redaction.
1069
- Method registration and deletion belong to no read slice and are never constructed.
1070
- For methods, `--select` selects output properties locally from the [reviewed authentication property sets](src/entra-auth-methods.ts); no `$select` is sent to Graph.
1071
- For the registration report, `--select` requests server properties; `--fields` must be a subset of the default or explicit selection for either command.
1072
- Only the registration report supports `--filter`, passed through as plain `$filter` with no `$count` or `ConsistencyLevel` contract.
1073
- The named-list caps, uniform `count` totals, cursors, null/missing preservation and 500-character text truncation described above also apply.
1074
- Resume method lists with the same `--user` ID or UPN, profile, scopes and API version; omit or repeat the original `--select`, and repeat local `--fields` and `--full` when wanted.
1075
- Delegated method reads default to `https://graph.microsoft.com/UserAuthenticationMethod.Read.All` (delegated self-reads may use `UserAuthenticationMethod.Read`) and registration reads default to `https://graph.microsoft.com/AuditLog.Read.All`, while application profiles use the configured `.default` audience.
1076
- Delegated callers acting on another user additionally need Global Reader, Authentication Administrator or Privileged Authentication Administrator for methods, and Reports Reader, Security Reader, Security Administrator or Global Reader for the report.
1077
- Denied reads name that role requirement instead of only the generic grant/role/licence cause.
1078
-
1079
- Log in with `https://graph.microsoft.com/AuditLog.Read.All`, then query sign-ins, directory audits and provisioning logs in bounded time windows (provisioning needs the extra scope below):
276
+ The profile schema is shipped as `axi-credential.schema.json`.
277
+ mg-axi retains `mode`; application profiles add `auth: "certificate" | "client-secret" | "federated"` and the corresponding `privateKey`, `clientSecret` or `token` SecretRef.
278
+ Delegated profiles created by the CLI use `token: {"stored": true}` for their MSAL cache.
279
+ Other SecretRefs are `{"env": "VARIABLE_NAME"}` and `{"file": "/absolute/local/path"}`.
280
+ Omitted secret fields default to their `MG_AXI_<FIELD>` environment reference.
281
+ Old `credentialRef` objects and SHA-1 `thumbprint` fields are rejected; update them to the flattened fields and 64-hex-digit `thumbprintSha256` after moving a legacy file.
282
+
283
+ ### Delegated credential storage
284
+
285
+ Explicit `auth login` stores a protected MSAL cache in the state directory's `credentials.json`, reported as `storage: protected-file`.
286
+ Windows and Linux use the same backend, with no keyring dependency or 2,560-byte cache limit.
287
+ Each entry is keyed by profile name and bound to its tenant and client IDs.
288
+ Changing that binding requires a new explicit login.
1080
289
 
1081
- ```sh
1082
- mg-axi login --profile soc --scopes https://graph.microsoft.com/AuditLog.Read.All
1083
- mg-axi entra sign-in list --profile soc --since 2026-09-01T00:00:00Z --limit 10
1084
- mg-axi entra sign-in list --profile soc --since 2026-09-01T00:00:00Z --filter "status/errorCode ne 0" --all
1085
- mg-axi entra sign-in show --profile soc --id <sign-in-id>
1086
- mg-axi entra directory-audit list --profile soc --since 2026-09-01T00:00:00Z
1087
- mg-axi entra directory-audit show --profile soc --id <directory-audit-id>
1088
- mg-axi entra provisioning list --profile soc --since 2026-09-01T00:00:00Z
1089
- mg-axi entra provisioning show --profile soc --id <provisioning-id>
1090
- ```
290
+ Each profile holds one account.
291
+ Every explicit login wipes that profile's protected and in-memory cache before acquiring replacement credentials and fails if the wipe cannot be verified.
292
+ Use separate profiles for separate identities; restore file access before retrying a failed wipe.
293
+ Only explicit login persists credentials; silent refresh keeps refreshed credentials in memory.
294
+ Access tokens are excluded from the persisted MSAL cache.
295
+ `auth logout` deletes the selected profile's cache and is idempotent; MSAL exposes no upstream token revocation API, so organizational revocation remains separate.
296
+ `auth status` reports the selected identity, Graph endpoint, secret source names or paths, and local availability without signing in or making a live request.
297
+ The old top-level `login` remains an alias for one minor release.
298
+ Tokens remain opaque and never appear in profile views, stdout or authentication diagnostics.
299
+ Credential acquisition is bound to the configured tenant/client and refreshes at a 60-second expiry margin.
1091
300
 
1092
- Provisioning logs additionally need `https://graph.microsoft.com/Directory.Read.All` alongside `AuditLog.Read.All`, plus a Microsoft Entra ID P1 or P2 licence on the tenant:
301
+ ### Application credentials and headless use
1093
302
 
1094
- ```sh
1095
- mg-axi login --profile soc --scopes https://graph.microsoft.com/AuditLog.Read.All,https://graph.microsoft.com/Directory.Read.All
1096
- ```
303
+ Application profiles use client credentials without a signed-in user and request the configured Graph `.default` audience.
304
+ An administrator must grant Graph application permissions on the app registration; delegated `--scopes` cannot narrow an application token.
305
+ Application profiles reject device-code opt-in and `login`, and acquisition failures never fall back to user authentication.
1097
306
 
1098
- Log lists always carry an explicit time bound: `--since` is required for a new query (with optional `--until` and `--filter` refinements), and resume reuses `--cursor` instead.
1099
- Resume validates the saved time bounds; a cursor from an unbounded raw query is rejected, so start a new query with `--since`.
1100
- Resume sign-in, directory-audit and provisioning lists with `--cursor -` and supply the returned cursor on stdin, for example `mg-axi entra sign-in list --profile soc --cursor - < cursor.txt`.
1101
- Small cursors can also use `--cursor <token>`; both forms enforce a 16 MB size ceiling.
1102
- `entra sign-in list` defaults to `id`, `createdDateTime`, `userPrincipalName` and `appDisplayName`; `entra directory-audit list` defaults to `id`, `activityDateTime`, `activityDisplayName` and `result`; `entra provisioning list` defaults to `id`, `activityDateTime`, `action` and `provisioningStatusInfo`.
1103
- All three log show commands default to the full reviewed property set.
1104
- `--select` requests properties from the [supported log property sets](src/entra-audit-logs.ts); `--fields` projects locally and must be a subset of the fetched selection.
1105
- Log reads truncate text longer than 500 characters, including nested values; `--full` restores complete text without lifting redaction, row caps or time bounds.
1106
- To replay a resumed result with `--full`, supply the original input cursor on stdin; the returned cursor continues after that result.
1107
- Graph omits CA policy detail without CA-data access, so an absent `appliedConditionalAccessPolicies` value reports its required policy permission and delegated role as unavailable rather than empty.
1108
- For CA detail, use Policy.Read.All or Policy.Read.ConditionalAccess in addition to AuditLog.Read.All; delegated callers also need Conditional Access Administrator, Global Reader, Security Administrator or Security Reader.
1109
- For delegated CA detail, log in and repeat the read with `--scopes https://graph.microsoft.com/AuditLog.Read.All,https://graph.microsoft.com/Policy.Read.All`.
1110
- Denied log reads name the operation's supported directory roles and the conservative P1/P2 deployment prerequisite instead of only the generic grant/role/licence cause.
1111
-
1112
- `entra application list` and `entra service-principal list` default to compact properties (`id`, `appId`, `displayName`); `appId` is the client ID, distinct from the object `id`, and both are included by default, while custom `--select` or `--fields` can omit either.
1113
- `entra application show --id <application-object-id>` defaults to the richer reviewed application set including `keyCredentials` and `passwordCredentials`.
1114
- `entra service-principal show --id <service-principal-object-id>` defaults to the richer reviewed service-principal set including `servicePrincipalType` and both credential collections.
1115
- Credential collections expose only expiry metadata (`keyId`, `displayName`, `startDateTime`, `endDateTime`); the shared session drops every other credential subfield before output or cursor buffering and applies the same filtering to buffered rows on resume, including older cursors.
1116
- Non-array credential collections become empty arrays, and non-object entries are dropped; other properties retain the null/missing behavior described above.
1117
- Secret-minting routes are never constructed.
1118
- `entra application owner list --application <application-object-id>` and `entra service-principal owner list --service-principal <service-principal-object-id>` list owners; rows carry `@odata.type` naming the owner kind.
1119
- `entra application federated-credential list` and `entra service-principal federated-credential list` default to `id`, `name`, `issuer` and `subject`: workload-identity trust metadata only, never secret material; each `show` defaults to the full reviewed set (`id`, `name`, `issuer`, `subject`, `description`, `audiences`).
1120
- There is no named command for the parenthesised `(appId='...')` or `(name='...')` lookups; filter the list instead. Those rows stay deferred (see the [READ-07 scope decisions](docs/coverage.md#read-07-application-lookups-scope-decisions)).
1121
- For consent grants, use the service-principal grant commands described above.
1122
- Application lists return `applications`, service-principal lists return `servicePrincipals`, owner lists return `owners`, federated lists return `federatedCredentials`, and single-object reads return `application`, `servicePrincipal` or `federatedCredential`.
1123
- App and service-principal `--select` accepts the [reviewed property sets](src/entra-apps.ts); `--fields` must be a subset of the fetched selection.
1124
- Owner rows default to `id`, `displayName` and `mail`, the only selectable owner properties; rows without non-null descriptive properties are preserved with a hint about limited consent or unset properties.
1125
- The named-list caps, uniform `count` totals, cursor resume rules and 500-character text truncation described above also apply to these reads; resume owner lists with the same `--application` or `--service-principal` object ID.
1126
- Delegated application reads, including federated-credential reads, default to `https://graph.microsoft.com/Application.Read.All`, while application profiles use the configured `.default` audience.
1127
-
1128
- Log in with `https://graph.microsoft.com/IdentityRiskyUser.Read.All`, `https://graph.microsoft.com/IdentityRiskEvent.Read.All` and `https://graph.microsoft.com/IdentityRiskyServicePrincipal.Read.All`, then triage risky users, risk detections, risky service principals and service-principal risk detections:
307
+ Choose exactly one credential provider:
1129
308
 
1130
309
  ```sh
1131
- mg-axi login --profile soc --scopes https://graph.microsoft.com/IdentityRiskyUser.Read.All,https://graph.microsoft.com/IdentityRiskEvent.Read.All,https://graph.microsoft.com/IdentityRiskyServicePrincipal.Read.All
1132
- mg-axi entra risky-user list --profile soc --limit 10
1133
- mg-axi entra risky-user show --profile soc --id <risky-user-id>
1134
- mg-axi entra risk-detection list --profile soc --since 2026-09-01T00:00:00Z --limit 10
1135
- mg-axi entra risk-detection show --profile soc --id <risk-detection-id>
1136
- mg-axi entra risky-service-principal list --profile soc --limit 10
1137
- mg-axi entra risky-service-principal show --profile soc --id <risky-service-principal-id>
1138
- mg-axi entra risky-service-principal history list --profile soc --service-principal <risky-service-principal-id>
1139
- mg-axi entra risky-service-principal history show --profile soc --service-principal <risky-service-principal-id> --id <history-item-id>
1140
- mg-axi entra service-principal-risk-detection list --profile soc --since 2026-09-01T00:00:00Z --limit 10
1141
- mg-axi entra service-principal-risk-detection show --profile soc --id <service-principal-risk-detection-id>
1142
- ```
1143
-
1144
- Delegated risky-user reads default to `https://graph.microsoft.com/IdentityRiskyUser.Read.All`; user and service-principal risk-detection reads default to `https://graph.microsoft.com/IdentityRiskEvent.Read.All`; risky-service-principal and history reads default to `https://graph.microsoft.com/IdentityRiskyServicePrincipal.Read.All`.
1145
- `--scopes` overrides those defaults; application profiles use the configured Graph `.default` audience and reject delegated scopes.
1146
- `entra risky-user list` and `entra risky-service-principal list` are state collections with an optional `--filter`; `entra risk-detection list` and `entra service-principal-risk-detection list` always carry an explicit time bound, so `--since` is required for a new query (with optional `--until` and `--filter` refinements) and resume reuses `--cursor` instead.
1147
- `entra risky-service-principal history list` and `history show` always carry `--service-principal` naming the owning risky-service-principal object ID.
1148
- Resume validates the saved detectedDateTime bounds; a cursor from an unbounded raw query is rejected, so start a new query with `--since`.
1149
- Resume risk lists with `--cursor -` and supply the returned cursor on stdin, for example `mg-axi entra risk-detection list --profile soc --cursor - < cursor.txt`.
1150
- `entra risky-user list` defaults to `id`, `userPrincipalName`, `riskLevel` and `riskState`; `entra risk-detection list` defaults to `id`, `detectedDateTime`, `userPrincipalName` and `riskLevel`.
1151
- `entra risky-service-principal list` and `history list` default to `id`, `displayName`, `riskLevel` and `riskState`; `entra service-principal-risk-detection list` defaults to `id`, `detectedDateTime`, `servicePrincipalDisplayName` and `riskLevel`.
1152
- All risk show commands default to the full reviewed property set.
1153
- `--select` requests properties from the [supported risk property sets](src/entra-risk.ts); `--fields` projects locally and must be a subset of the fetched selection.
1154
- Risk reads truncate text longer than 500 characters, including nested values such as `location` and the `additionalInfo` JSON string; `--full` restores complete text without lifting redaction, row caps or time bounds.
1155
- See the [access and licence contract](docs/graph-coverage.md#licence-matrix-by-area) for the riskyUsers P2 requirement, risk-detection P1/P2 detail boundaries and the Workload Identities Premium requirement behind the workload-identity reads.
1156
- Limited views stay limited: a premium detection without P2 detail reports `riskEventType` generic, hidden risk levels report the licence boundary instead of the level, service-principal detections report `riskDetail` and `riskLevel` hidden without Workload Identities Premium, and a null detection `correlationId` means no sign-in is associated.
1157
- To correlate a detection to sign-ins, use `risk-detection show` or select `activityDateTime`, then filter the sign-in list above on the detection's `userPrincipalName` in that activity window; sign-in reads require the separate `AuditLog.Read.All` login above, and there is no riskySignIns endpoint.
1158
- To correlate a workload detection, filter `service-principal-risk-detection list` on the principal's `servicePrincipalId` in the detection window, or list that principal's risk history with `risky-service-principal history list --service-principal <risky-service-principal-id>`; workload reads never confirm, dismiss or remediate risk.
1159
- WRITE-05 adds a named write: `mg-axi entra risky-user dismiss --user <risky-user-id>` dismisses one user's risk through `POST /identityProtection/riskyUsers/dismiss` with a single-element `{ "userIds": [...] }` body.
1160
- There is no bulk form: `--user` takes exactly one ID and a comma-separated list is a usage error.
1161
- The command supports only `--api-version v1.0`; beta writes are rejected before credentials or HTTP.
1162
- Without `--execute` the command previews the user's current risk state read through the risky-user show route and journals nothing; an already-dismissed user is a no-op with exit 0.
1163
- Preview also requires the write-enabled profile and operation allowlist described below.
1164
- Dismissal is disruptive: every `--execute` run needs `--confirm '<risky-user-id>'` repeating the target exactly, including already-dismissed states.
1165
- Dismissal answers 204 with an empty body, so after a send the command rereads the user and reports a `WRITE_CONFLICT` when the state is not dismissed; a failed verification read reports `OUTCOME_UNKNOWN`.
1166
- A timeout or 5xx after send is `OUTCOME_UNKNOWN` with no replay: read back the user before doing anything else.
1167
- Fresh reads do not make dismissal atomic: no ETag condition is sent, so another actor can change the risk state between the read, POST and verification.
1168
- Dismissal is not remediation: it records the risk as dismissed without resetting credentials or revoking sessions.
1169
- Delegated dismissal requests `IdentityRiskyUser.ReadWrite.All` while preview, pre-send and verification reads request `IdentityRiskyUser.Read.All`; credentials are acquired silently.
1170
- Use explicit `mg-axi login --profile soc --scopes https://graph.microsoft.com/IdentityRiskyUser.ReadWrite.All,https://graph.microsoft.com/IdentityRiskyUser.Read.All` to sign in for the write and its prerequisite reads.
1171
- Application profiles use the configured Graph `.default` audience with `IdentityRiskyUser.ReadWrite.All` admin-consented on the app registration; the prerequisite reads also require a [supported risky-user read permission](https://learn.microsoft.com/en-us/graph/api/riskyuser-get?view=graph-rest-1.0).
1172
- The dismissal command rejects caller-supplied `--scopes`.
1173
- Delegated callers additionally need `Security Administrator`, and the riskyUsers API requires a Microsoft Entra ID P2 licence; 403 denials surface the permission, role and licence rules because a 403 never says which prerequisite is missing.
1174
- Risk reads never confirm, dismiss or remediate risk.
1175
-
1176
- Configure the registration's Mobile and desktop applications redirect URI as `http://localhost` for browser login.
1177
- The first created profile is the default; `--profile` selects another identity explicitly.
1178
- Configuration defaults to `~/.mg-axi/config.json`; `MG_AXI_CONFIG` selects a separate configuration file.
1179
- Version 1 stores tenant/client/cloud, delegated or application mode, enabled packs, preview/sensitive policy, device-code opt-in, a unique credential reference and an optional write policy.
1180
- Unknown fields, unsupported versions/clouds and inlined credential material are rejected with recovery guidance.
1181
- Creation never overwrites an existing profile.
1182
- Preview is disabled and sensitive areas are empty in newly created profiles.
1183
-
1184
- Writes stay disabled unless a human hand-edits a `writes` object into the profile file: `{ "allowWrites": true, "operations": ["<operation-name>"] }`.
1185
- The object accepts only `allowWrites` (boolean) and `operations` (1 to 64 nonempty operation names, each at most 256 characters), including when `allowWrites` is false.
1186
- No command writes that object, and `MG_AXI_READ_ONLY=1` overrides any opt-in.
1187
- For the supported membership write, see the group usage above.
1188
- Named writes run through the shared coordinator under the [named-write execution contract](docs/execution.md#named-writes).
1189
- WRITE-02 binds the `entra.user.update` operation name: hand-enable account writes with `{ "allowWrites": true, "operations": ["entra.user.update"] }`.
1190
- WRITE-03 binds the `entra.user.revokeSessions` operation name: hand-enable session revocation with `{ "allowWrites": true, "operations": ["entra.user.revokeSessions"] }`.
1191
- WRITE-04 binds the `entra.conditional-access.policy.update` operation name: hand-enable policy writes with `{ "allowWrites": true, "operations": ["entra.conditional-access.policy.update"] }`.
1192
- WRITE-05 binds the `entra.risky-user.dismiss` operation name: hand-enable risk dismissals with `{ "allowWrites": true, "operations": ["entra.risky-user.dismiss"] }`.
1193
- The journal defaults to `~/.mg-axi/writes.log`; a nonblank `MG_AXI_WRITE_LOG` overrides that path.
1194
-
1195
- Browser login uses Microsoft's [MSAL interactive API](https://learn.microsoft.com/en-us/entra/msal/javascript/node/acquire-token-requests) and PKCE.
1196
- Request delegated Graph scope names explicitly with `--scopes`, separated by commas.
1197
- Use full `https://graph.microsoft.com/` scope names; delegated login rejects `.default`.
1198
- The dedicated registration needs corresponding delegated consent, and operations can additionally require user roles.
1199
- Login never changes app registration or requests blanket directory write consent automatically.
1200
- Device code requires creating the profile with `--allow-device-code` and selecting `--method device-code` during login, only when organization policy permits it.
1201
- Browser failure never falls back to device code.
1202
- Only explicit login can open a browser or display the device challenge on stderr.
1203
- Ordinary credential acquisition uses silent refresh and returns actionable errors when login, consent or policy intervention is required.
310
+ # Certificate: register the matching public certificate on the app registration
311
+ mg-axi profile create --name daemon --tenant <tenant-id> --client <client-id> --cloud commercial --mode application --certificate-thumbprint-sha256 <64-hex-digit-thumbprint>
1204
312
 
1205
- Read reviewed raw Graph data without waiting for a named command:
313
+ # Workload federation: configure the app registration's federated credential mapping
314
+ mg-axi profile create --name batch --tenant <tenant-id> --client <client-id> --cloud commercial --mode application --federated
1206
315
 
1207
- ```sh
1208
- mg-axi api get /users --scopes https://graph.microsoft.com/User.Read.All
1209
- mg-axi api get /groups --odata '$filter=securityEnabled eq true&$top=5' --scopes https://graph.microsoft.com/GroupMember.Read.All
1210
- mg-axi api get /identity/conditionalAccess/policies --scopes https://graph.microsoft.com/Policy.Read.All
1211
- ```
316
+ # Headless client secret: supply the value through the named environment variable
317
+ mg-axi profile create --name svc --tenant <tenant-id> --client <client-id> --cloud commercial --mode application --client-secret-env MG_AXI_CLIENT_SECRET
318
+ mg-axi profile show --profile svc
319
+ ```
320
+
321
+ By default, certificate keys come from `privateKey: {"env": "MG_AXI_PRIVATE_KEY"}`.
322
+ Profile creation records the reference and thumbprint; it does not import a key and there is no CLI key-import command.
323
+ Provision keys through protected storage tooling, never argv or profile JSON.
324
+ Certificate acquisition fails if the referenced key is unavailable.
325
+
326
+ For headless certificate holders, add `--certificate-key-file <path>` or `--certificate-key-env <variable-name>`.
327
+ Client secrets similarly accept `--client-secret-file <path>` instead of `--client-secret-env`.
328
+ All credential files, including federated assertions, must use absolute local paths.
329
+ On Unix, they must be regular files owned by the current user with no group or other permission bits; 0400 and 0600 are accepted.
330
+ Configuration must not be writable by group or other.
331
+ On Windows, writes apply a protected DACL for the current user, SYSTEM and Administrators; reads reject reparse points, and `auth status` and `doctor` verify the DACL.
332
+ UNC and device paths are rejected.
333
+ Missing, empty or exposed holders fail closed without disclosing values.
334
+ PEM environment values may contain escaped `\n`; passphrase-protected keys are unsupported.
335
+ Prefer certificates or federation over bearer client secrets, and rotate secrets on a schedule.
336
+
337
+ Federation reads the assertion file on every acquisition so projected tokens can rotate.
338
+ Use `--federated-token-file <absolute-path>` for a projected assertion file, or set the default `MG_AXI_TOKEN` assertion value through the environment.
339
+ When the token reference is the default `MG_AXI_TOKEN` env holder and that variable is unset, `AZURE_FEDERATED_TOKEN_FILE` stays accepted as a deprecated alias for one minor release with a stderr notice; `MG_AXI_TOKEN` takes precedence and the alias must name an absolute file path.
340
+ References, rather than values, are stored in configuration; rotation updates the environment or file without changing the profile.
341
+ Application credentials remain in process memory and are reacquired at a 60-second expiry margin.
342
+ See [headless credential guidance](docs/headless-credentials.md) for holder examples.
1212
343
 
1213
- `api get` accepts only GET routes in the [reviewed route catalogue](src/api.ts), which owns route-specific query keys, `$select` fields and access constraints.
1214
- OData parameters use `--odata`; `--query` is reserved for output queries and is not implemented here.
1215
- Omitting `$select` selects the route's default reviewed fields, and every response is filtered to the selected subset before output.
1216
- Raw PKI and certificate-authority list/show reads use the certificate-auth paths in the [reviewed route catalogue](src/api.ts); their `$count` routes are available only through the named count commands above.
1217
- Raw certificate-authority defaults omit `certificate`; request it explicitly with `--odata '$select=certificate'`, subject to the raw text truncation described below.
1218
- For `/users/<user-id>/authentication/methods`, `$select` is the only supported OData parameter and selects output properties locally; an explicit selection restricts output to that subset.
1219
- Method requests omit `$select` on initial requests, continuations, redirects and retries, while cursors preserve the local selection.
1220
- Other routes send `$select` to Graph.
1221
- Relationship expansion (`$expand`) is unavailable.
1222
- Unreviewed, secret-value, mail/file-content, beta and write routes fail before credentials, and pack, preview and sensitive-area policy still runs in the shared session.
1223
- Delegated raw reads require explicit `--scopes` from the supported read choices described above; application profiles use the configured `.default` audience and reject `--scopes`.
1224
- Collections return `returned`, `complete` and `value`, default to 100 rows, and follow pages within budget under `--all`.
1225
- `--limit` and `--all` cannot be combined.
1226
- Completion describes pagination, not visibility of every directory object; group-member results include a warning for the [v1.0 service-principal limitation](docs/graph-coverage.md#licensing-and-completeness-findings), even when `complete` is true.
1227
- Strings longer than 500 characters are truncated; `--full` removes string truncation without disabling redaction, reviewed-field filtering or row caps.
1228
- Partial results include a `cursor` preserving buffered rows and the next page.
1229
- Resume with `--cursor -` and supply the cursor token on stdin under the same collection path, profile and scopes, optionally with `--all` or a new `--limit`.
1230
- For example, `mg-axi api get /users --cursor - --all --scopes https://graph.microsoft.com/User.Read.All < cursor.txt` reads a saved token through stdin.
1231
- Small tokens can also use `--cursor <token>`; both input forms use the same validation and a 16 MB size ceiling.
1232
- Omit `--odata` on resume to reuse the original query; supplying a different query is refused.
1233
-
1234
- Delegated MSAL caches use the OS credential store through optional `keytar`, with login reporting `storage: os-protected`.
1235
- If `keytar` cannot load, caches remain in process memory and login reports `storage: session-only`; authentication then lasts only for that process.
1236
- Installing with `--ignore-scripts` can leave the native module unavailable.
1237
- Session-only mode uses an MSAL client without a persistence plugin and is available only when no usable protected store exists.
1238
- On Windows, a serialized cache exceeding 2,560 UTF-8 bytes cannot be persisted; after verifying a full protected-cache wipe, login or silent refresh succeeds with the authenticated cache in session-only memory.
1239
- If that wipe fails or cannot be verified, authentication fails without switching storage modes.
1240
- Store read, write or invalidation failures fail authentication with `LOGIN_FAILED` during login or `AUTH_REQUIRED` during silent acquisition, rather than switching storage modes.
1241
- Each profile holds one account.
1242
- Every explicit login wipes that profile's entire in-memory and protected MSAL cache before acquiring credentials, and fails if the wipe cannot be verified.
1243
- Switching accounts within a profile requires a fresh login; use separate profiles for separate identities.
1244
- Restore OS credential store access before retrying a failed wipe.
1245
- An inaccessible credential service on a headless system can therefore block authentication even when the native module loads.
1246
- There is no plaintext credential-cache fallback.
1247
- Tokens remain opaque and never appear in profile views, stdout or authentication diagnostics.
1248
- The delegated credential service binds account context to the configured tenant/client and refreshes at a 60-second expiry margin.
1249
- Tests use fake credential providers, fake time and real MSAL cache handling with fake network and storage boundaries; they never sign in or contact a real token endpoint.
344
+ ## Development & Testing
1250
345
 
1251
- Create an application profile with an organization-owned app registration and exactly one credential provider:
346
+ Use Node.js 22.12.0 or later and the pinned pnpm 10.34.6 from [package.json](package.json).
347
+ From a checkout root:
1252
348
 
1253
349
  ```sh
1254
- mg-axi profile create --name daemon --tenant <tenant-id> --client <client-id> --cloud commercial --mode application --certificate-thumbprint <40-hex-digit-thumbprint>
1255
- mg-axi profile create --name batch --tenant <tenant-id> --client <client-id> --cloud commercial --mode application --federated
1256
- mg-axi profile show --profile daemon
350
+ corepack pnpm install --frozen-lockfile --ignore-scripts --config.confirm-modules-purge=false
351
+ corepack pnpm build
352
+ node dist/bin/mg-axi.js --version
353
+ node dist/bin/mg-axi.js entra user list --help
354
+ corepack pnpm test
355
+ corepack pnpm lint
356
+ corepack pnpm run docs:check
1257
357
  ```
1258
358
 
1259
- Omitting `--mode` selects delegated mode; certificate, client-secret and federation flags require application mode.
1260
- Application profiles reject `--allow-device-code`, and `login` with either login method exits 2 before authentication.
1261
- For certificates, register the matching public certificate on the app registration.
1262
- By default the provider expects a PEM private key in the OS credential store under service `mg-axi` and account equal to the profile's `credentialRef.key`, shown by `profile show`.
1263
- Profile creation records the thumbprint and reference; it does not import the private key, and there is no CLI key-import command.
1264
- Provision that key through protected storage tooling; never put private keys in argv or profile JSON.
1265
- Keychain certificate acquisition fails if `keytar` or the referenced key is unavailable; file and environment key holders below authenticate session-only instead.
1266
- For workload federation, configure the app registration's federated credential mapping for the workload and set `AZURE_FEDERATED_TOKEN_FILE` to its assertion file.
1267
- The provider reads the file on each assertion request so projected tokens can rotate; missing or empty assertions fail authentication.
1268
- `--federated-token-file-env <NAME>` names a different holder variable when the workload does not use the default.
1269
-
1270
- Headless systems (containers, CI, services) have no OS keychain, so application profiles also accept file and environment references that are read at acquisition time:
1271
-
1272
- ```sh
1273
- mg-axi profile create --name svc --tenant <tenant-id> --client <client-id> --cloud commercial --mode application --client-secret-env MG_AXI_CLIENT_SECRET
1274
- mg-axi profile create --name daemon-file --tenant <tenant-id> --client <client-id> --cloud commercial --mode application --certificate-thumbprint <40-hex-digit-thumbprint> --certificate-key-file /run/secrets/mg-axi-key.pem
1275
- mg-axi profile create --name daemon-env --tenant <tenant-id> --client <client-id> --cloud commercial --mode application --certificate-thumbprint <40-hex-digit-thumbprint> --certificate-key-env MG_AXI_PRIVATE_KEY
1276
- MG_AXI_CLIENT_SECRET=<secret> mg-axi doctor --profile svc
1277
- ```
359
+ Tests use fixture credentials and transports without signing in or contacting real tenants.
360
+ The test script limits concurrency to four files.
361
+ To use the built checkout binary on PATH, run `npm link --ignore-scripts` from the checkout root.
362
+ Otherwise substitute `node dist/bin/mg-axi.js` for `mg-axi` in examples.
1278
363
 
1279
- A profile stores only the reference (an env var name or a file path), never the value.
1280
- The environment supplies the value on each acquisition, so rotation updates the env var or file contents with no profile change.
1281
- Relative paths resolve against the working directory at acquisition time.
1282
- Key and secret files must be regular files owned by the current user with mode 0600, never symlinks; Windows skips the ownership and mode checks.
1283
- A missing, empty or exposed holder fails closed with `AUTH_REQUIRED` naming only the reference, never its value.
1284
- PEM values from env vars may use escaped `\n` newlines; passphrase-protected keys are unsupported.
1285
- A client secret is weaker than a certificate or federated credential: it is a bearer string with no proof of possession.
1286
- Prefer certificates or federation where the platform allows them, and rotate secrets on a schedule.
1287
- Application credentials remain in process memory and are reacquired at a 60-second expiry margin.
1288
- The service requests only `https://graph.microsoft.com/.default`, representing the app registration's admin-consented Graph application permissions, with no signed-in user.
1289
- Ask an administrator to grant those permissions on the configured app registration; per-command delegated scopes cannot narrow the application token.
1290
- Acquisition failures return `AUTH_REQUIRED` with consent and credential-holder guidance, without user or device-code fallback.
1291
-
1292
- Run `corepack pnpm build`, `corepack pnpm test` (test files run at most 4 at a time via `--test-concurrency=4` in the `test` script) and `corepack pnpm lint` for shell validation.
1293
- The [implementation plan](PLAN.md) remains the design authority.
1294
-
1295
- The pinned [Entra operation inventory](docs/inventory.md) defines the INV-01 discovery boundary and schema for later build slices.
1296
-
1297
- ## Release
1298
-
1299
- This is the supported Entra read and gated write surface, not full Entra coverage.
1300
- See the generated [capability report](docs/coverage.md) for implemented reads, writes and discovery dispositions, and the [skill command table](skills/mg-axi/SKILL.md#orientation) for all executable leaves, including local commands.
1301
- The package is published to npm as `@knowttl/mg-axi` with public access.
1302
- Install it globally with `npm install --global @knowttl/mg-axi`, or run without a global install via `npx -y @knowttl/mg-axi --help`.
1303
- The `.github/workflows/release-please.yml` workflow manages release PRs on `main` and builds, checks and publishes newly created releases with provenance using npm trusted publishing (OIDC) from the public repository.
1304
- Publication is skipped when the release version already exists on npm.
1305
- Version `0.1.0` was bootstrap-published on npm, and release-please continues from that version.
1306
- The packed files are `dist`, the discovery inventory, `skills/mg-axi`, `docs/coverage.md` and this README.
1307
- Follow the [checkout instructions](#mg-axi) to install dependencies, build, link the binary and run the version probe.
1308
-
1309
- After linking the checkout binary, `mg-axi setup` shows the build steps, the selected configuration path, the configured profiles and the capability summary.
1310
- It writes nothing, signs in nowhere and installs no hooks; ordinary commands never gain installation side effects.
1311
- Create profiles with `mg-axi profile create`, sign delegated profiles in with `mg-axi login`, and check access with `mg-axi doctor`.
1312
- Doctor checks `--profile <name>` when supplied, otherwise the configured default, or all configured profiles when no default exists.
1313
- Doctor performs one bounded `entra user list --limit 1` read per profile with silent credential acquisition only: it never opens a browser, never shows a device-code challenge, never auto-installs and never enables writes.
1314
- Missing profiles, an unknown selected profile or invalid configuration fail before Graph reads.
1315
- Read failures report per profile with rerun guidance and a nonzero exit.
1316
-
1317
- The installable skill lives at [skills/mg-axi/SKILL.md](skills/mg-axi/SKILL.md).
1318
- Agent discovery has two install paths, and either suffices alone.
1319
- Install the skill explicitly with `npx skills add knowttl/mg-axi --skill mg-axi`; the setup command only shows guidance and does not install skills.
1320
- For ambient context at every agent session start instead, install the CLI globally and opt into the session hook:
1321
- ```sh
1322
- npm install --global @knowttl/mg-axi
1323
- mg-axi setup hooks
1324
- ```
1325
- This installs a `SessionStart` hook for Claude Code, Codex and OpenCode that prints a short local-only summary (configured profile names with tenant labels, auth-cache presence, write posture and version).
1326
- The hook makes no network, Graph or sign-in call, and prints a short `not configured` record with exit 0 when unconfigured.
1327
- Restart the agent session after running the installer.
1328
- No other ordinary command installs hooks, plugins or configuration.
1329
- The skill file is generated in full from the template in [src/docs.ts](src/docs.ts) and the command catalogue; [docs/coverage.md](docs/coverage.md) also uses the discovery inventory.
1330
- After building, regenerate both with `corepack pnpm run docs:generate` and verify freshness with `corepack pnpm run docs:check`; the release publish job runs the freshness check.
1331
- Critical journeys stay packaged offline: `test/pack.test.mjs` drives setup, doctor and the user, group, Conditional Access and sign-in reads through the packaged executable with fixture credentials and blocked networking.
364
+ The agent skill and capability report are generated from the command catalogue, documentation template and discovery inventory.
365
+ After changing their sources, run `corepack pnpm run docs:generate`, then `corepack pnpm run docs:check`.
366
+ The [implementation plan](PLAN.md), [operation inventory](docs/inventory.md) and [execution contract](docs/execution.md) describe design and coverage boundaries.