# CLI and background-process integration

<!-- m7-identity-discovery:begin -->
## Connect to M7 Identity

Issuer: <https://sso.user.m7.org>

Discovery JSON: <https://sso.user.m7.org/.well-known/openid-configuration>

- [Discovery and signing keys](https://m7.org/docs/api/sso.user.m7.org/discovery-and-keys.md)
- [Integration quickstart](https://m7.org/docs/api/sso.user.m7.org/quickstart.md)
- [Provider profile](https://m7.org/docs/api/sso.user.m7.org/provider-profile.md)
- [M7 Identity SDK and current downloads](https://m7.org/docs/sdk/m7-identity/)

Live discovery supplies endpoint locations and advertised capabilities. Use the
provider profile for the integration contract and current testing status.
<!-- m7-identity-discovery:end -->

The prepared Token/PHP 0.1.5-rc.1 candidate adds receiver-authenticated organization
lookup, ACL member management and canonical org principal references. The verified
public download remains 0.1.4. See [candidate support status](https://m7.org/docs/sdk/m7-identity/README.md#prepared-tokenphp-update)
before selecting those additions.

For optional encrypted ID tokens, JARM and UserInfo, see
[PHP response encryption](https://m7.org/docs/sdk/m7-identity/response-encryption.md). It requires the documented
Token/PHP 0.1.4 and optional native runtime. Ordinary supported flows do not
need the native extension. See the [release status](https://m7.org/docs/sdk/m7-identity/README.md#package-and-release-status). The CLI does not expose recipient configuration options.
For the separate C library and PHP extension, use the
[native component guides](https://m7.org/docs/sdk/m7-identity/README.md#optional-native-runtime), which link
installation, compatibility and release status for each component.

`cli-php` is the dedicated M7 Identity command-line adapter. It supplies the
`m7-identity` executable over the storage-neutral OAuth and identity protocol
operations in `token-php`; it is not a second protocol implementation. The CLI
package is available as the stable 0.1.2 artifact and requires the stable
`token-php >=0.1.3 <0.2.0` package.

The stable executable supports these command families:

| Command | Supported CLI responsibility |
| --- | --- |
| `app` | Create, inspect, update, copy, and delete protected local OAuth app records. |
| `profile` | Manage named grant profiles beneath an app. |
| `password` | Store or import an optional OAuth client password without treating it as an M7 account password. |
| `token` | Import, inspect, promote, and delete protected complete token-bundle generations. |
| `client-credentials` | Acquire and optionally persist an application access-token bundle through `token-php`. |
| `device-code` | Start or resume device authorization, show the approval coordinates, poll on the authority-directed schedule, and optionally persist the resulting bundle. |
| `token-exchange` | Exchange an original user access token for one approved connected target using RFC 8693. |
| `exchange` | Exchange a complete refresh package and persist the active or pending replacement when an app is selected. |
| `ack` | Acknowledge a retained pending package and promote the exact stored generation only after authority success. |
| `ensure` | Reuse or renew a managed app token and durably apply any required bundle transition. |
| `request` | Ensure a managed token or accept protected explicit credentials, then send one authenticated HTTPS resource request. This is the authenticated-request command. |

CLI/PHP 0.1.2 with Token/PHP 0.1.4 supports
[RFC 8693 Token Exchange](https://m7.org/docs/sdk/m7-identity/cli/token-exchange.md)
through `m7-identity token-exchange` and `M7IdentitySDK::tokenExchange()`.
This calls a connected service on a user's behalf. It is distinct from the
`exchange` refresh command. Install the matching releases and retain the
exchanged bundle separately from the source login. The linked guide also explains `client_id`,
`act` and the optional `m7.target_client_id` validator policy.

The `app`, `profile`, `password`, and `token` commands operate on the protected
app -> grant -> named-profile keychain beneath `~/.m7/identity`. An app name is
a store-wide unique local alias for a registered client ID; it is not scoped to
one grant or profile. `app list` is the safe alias-to-client-ID lookup surface,
while operational commands select the explicit app alias. A whole-app copy may
intentionally give the same client ID another alias, so a client ID is not
assumed to identify one unique local app.

Ordinary selection defaults to the profile named `default`. App and profile
copy require explicit `--src` and `--dst`, deep-copy every protected file in
scope—including passwords and other secrets, active tokens, pending packages,
and history—and atomically refuse existing destinations. Copy does not print
credentials, hard-link protected files, or alter authority state. Keychain
directories use mode `0700`; records, locks, password files, and token bundles
use mode `0600`.

For repeatable registration synchronization, `app upsert` and `profile
upsert` create missing records or replace their complete editable definitions.
They never recursively merge stale fields. Existing passwords, token
generations, history, and creation timestamps remain untouched; rebinding an
existing local app name to a different client ID is refused.

The client-credentials command consumes an app and its default or named
client-credentials profile:

```sh
./vendor/m7-identity-cli-php-0.1.2/bin/m7-identity client-credentials \
  --app user.m7.org-cc \
  --interactive
```

Resolution is environment, command option, selected profile, then prompt or
default. Profile configuration preserves absent versus explicit-null fields;
null suppresses an optional prompt, while a required null fails. An optional
OAuth client password can be stored per profile through a hidden prompt or
imported JSON, and client credentials consumes it automatically. A successful
app-selected acquisition stores the complete response under that profile
before output and archives the previous active generation.

```sh
./vendor/m7-identity-cli-php-0.1.2/bin/m7-identity password set \
  --app user.m7.org-cc \
  --grant client-credentials \
  --password
```

Protected file and stdin imports avoid argv exposure. Inline `--json` import is
also supported when the operator accepts shell-history and process-listing
exposure. The password command stores an OAuth client credential, never an M7
user's account password, and never reveals it in status or mutation output.

This public guide covers client credentials, device authorization,
authenticated requests, managed ensure, profile/password/token setup, copying,
protected bundle handling, strict rotation, regular ACK, and fault-tolerant ACK
supersession. Each command also includes concise examples in its built-in
`--help` output.

For HS256/384/512 local verification, see [HMAC setup and
acceptance](../README.md#hmac-verification). Token acquisition, refresh
and introspection remain separate from local MAC verification. This does not
add a CLI local-verification option.
Public native clients using `none` must select asymmetric ID-token signing;
embedding a shared secret does not establish confidentiality. The User.M7/SSO
guard against `none` plus HMAC passed scoped production acceptance on September
20; the linked guide preserves the legacy-record test limit. Public device
authorization remains supported with asymmetric signing; local MAC support
does not override SSO client eligibility.

## Current-source native verification

Updated `token-php` source can verify all 15 asymmetric profiles, including
ML-DSA, with an explicit allowlist, compatible public-key resolution and the
optional `m7crypto` extension/provider for additional profiles. An unavailable
backend fails verification explicitly. Token/PHP 0.1.4 includes this
integration; the earlier Token/PHP 0.1.2 ZIP is unchanged. The native wrapper
currently targets PHP 8.4+, independently of the base SDK's PHP 8.1+ requirement.

The tested ML-DSA device pickup/refresh path receives tokens as JSON and uses
process-side storage rather than the Web SDK's token cookies. Keep complete
bundles protected and account for resource-header, proxy and storage limits.
This does not establish ML-DSA browser session compatibility. See
[native verification and post-quantum use](https://m7.org/docs/sdk/m7-identity/README.md#additional-signing-profiles).

## Capabilities

The stable `token-php` 0.1.4 release provides:

* strict compact JWT parsing;
* trusted-certificate resolution and RSA signature verification;
* issuer, time, audience, claims, and configured-scope validation;
* optional DPoP proof validation for a known request context;
* portable cache interfaces for certificates and successful validation;
* OAuth client-credentials access-token acquisition for confidential clients;
* OAuth device authorization and one-attempt token polling for public or
  confidential clients;
* complete M7 refresh-package exchange and two-phase ACK activation for
  user-authorized clients;
* storage-neutral access-token ensure across client credentials and refresh;
* schema-neutral authenticated HTTPS resource requests using complete
  Bearer/DPoP bundles and optional fingerprint binding;
* OAuth token introspection for current authority state; and
* OpenID Connect UserInfo retrieval with optional exact subject binding.

UserInfo accepts `application/json` or verified RS256/RS512
`application/jwt`. Signed responses are checked through the configured
issuer's discovery document and JWKS before claims are returned.

The root `M7IdentitySDK` facade exposes `validate()`, `clientCredentials()`,
`deviceAuthorization()`, `deviceToken()`, `refreshToken()`,
`acknowledgeToken()`, `ensureAccessToken()`, `request()`, `introspect()`, and
`userinfo()` for
these primary operations. Granular parser, resolver, verifier, policy,
audience, scope, transport, and report types remain available for advanced use.

## Capability boundary

The `cli-php` adapter owns environment, option, and profile resolution;
controlling-terminal prompts; the device polling loop and schedule; stdout,
stderr, output formats, and exit status; and its protected app/profile/password
and token-bundle keychain. Its protocol commands then call the corresponding
`M7IdentitySDK` operations in `token-php`.

`token-php` remains storage-neutral. It does not read CLI environment
variables, prompt, render command output, choose a keychain or database, or run
the device polling schedule. It performs one protocol attempt per call,
validates the response, and returns reports and complete bundles for its caller
to persist. Even its ACK-aware ensure operation accepts a caller-owned durable
checkpoint callback rather than selecting storage itself. The library owns
endpoint normalization, reusable client authentication, request construction,
bounded HTTPS transport, and protocol-response validation.

Neither the CLI nor the token library currently supplies authorization-code
acquisition or outbound DPoP proof generation. A DPoP-required caller must
create a fresh proof outside these packages and provide it for every applicable
request or device polling attempt.

Supply tokens through the consuming process's established protected channel.
Prefer not to place access tokens, refresh credentials, client secrets, or
DPoP proofs in command arguments, shell history, logs, URLs, or world-readable
files. The explicit `password import --json` escape hatch is available when
the operator accepts its argv exposure; `token import --json` has the same
exposure for complete bundles. Neither is used implicitly.

The CLI also has a local protected token store beneath
`<app>/<grant>/<profile>/tokens/`. Import a complete mode-0600 bundle with
`m7-identity token import --app APP --grant GRANT --file PATH`; omission of
`--profile` selects `default`. Normal imports become active and archive the
preceding generation. ACK envelopes remain pending. `token status` returns
metadata only, and `token promote --id ID` performs only the local commit after
a separately successful authority ACK. App-selected client credentials and
device acquisition save automatically. Managed exchange performs active or
pending rotation, and managed ACK promotes the exact acknowledged pending
generation.

Protected file and bounded-stdin inputs avoid plaintext credential arguments.
Credential-bearing output goes only to stdout when explicitly requested;
diagnostics go to stderr, and status/mutation summaries disclose metadata only.
Do not redirect output onto its current input file because the shell truncates
the destination before the command can validate or persist a replacement.

## OAuth acquisition and refresh lifecycle

The released protocol and CLI layers support these grant/authentication
combinations:

| Operation | Client and bootstrap | Supported client authentication |
| --- | --- | --- |
| Client credentials | Confidential machine client acting as itself; no user or prior bundle is involved. | `client_secret_basic` or `client_secret_post`; public-client `none` is rejected. |
| Device authorization and polling | Required bootstrap for a native user client. The person authenticates in the M7 browser page; the CLI never collects the account password. | `none` for a public native client, or `client_secret_basic`/`client_secret_post` for a confidential device client. |
| Refresh-package exchange | Requires the complete bundle established by device authorization. It cannot bootstrap a native client from no state. | `none`, `client_secret_basic`, or `client_secret_post`, matching the registered device client. |
| Token acknowledgement | Applies only to a retained pending ACK package. The activation credential in that package authorizes ACK; a DPoP-bound lineage also requires a fresh caller-generated proof. | No separate client password is added to the ACK request. |

Token/PHP 0.1.4 and CLI/PHP 0.1.2 also support `tls_client_auth` and
`self_signed_tls_client_auth` for confidential OAuth clients. Supply a matching
PEM certificate and protected private-key file; an encrypted key can use a
separately protected passphrase. Client credentials, device, refresh and ensure accept
`--client-certificate-file`, `--private-key-file`, and optional
`--private-key-passphrase-file`; private-key and passphrase files must not be
symlinks or grant group/other access. The separate
[token-exchange command](https://m7.org/docs/sdk/m7-identity/cli/token-exchange.md#use-the-cli) uses `--certificate-file`.
Do not
combine certificate authentication with a client secret.

Built-in consumer and tenant selectors use their certificate-requesting mTLS
endpoint aliases. Resource and UserInfo calls with certificate-bound access
tokens also need the certificate/key and an HTTPS endpoint that requests the
certificate; the issuer remains the configured OIDC issuer. This transport
support does not require the optional M7 Crypto extension. The examples below use Basic/Post or public asymmetric `none`; select mTLS
only when it matches the registered client and resource.

A complete native token bundle keeps the access token and type together with
the refresh token and its `binding_chain` and `binding_link`; pending ACK
packages also retain activation data and the complete active predecessor.
Persist and transition the whole bundle. A bare access token is insufficient
for refresh, ACK, or durable recovery.

Refresh exchange accepts these lifecycle modes:

| Mode | Lifecycle |
| --- | --- |
| `static` | Issue a new access token without rotating the refresh credential. |
| `strict_rotation` | Return an active replacement and immediately retire the prior refresh credential; persist the replacement atomically because a lost response can strand the client. |
| `grace_rotation` | Return an active replacement while the predecessor remains usable only for the authority's bounded grace/recovery window. |
| `ack` | Return one pending successor while retaining the predecessor as active. A second refresh from that predecessor is serialized as `refresh_in_progress` until the pending package is acknowledged or expires. |
| `ack_supersede_pending` | Normally begin from the retained active package, atomically disable any older never-active pending child, and return the newest pending successor. Persist and acknowledge only that newest package. |

Both ACK modes require the caller or CLI to durably store the complete pending
successor before acknowledgement and to promote it only after ACK succeeds.
`ack_supersede_pending` is a normal fault-tolerant lifecycle, not a recovery-only
operation; it can start with no pending package or replace an older
unacknowledged child while the active predecessor remains retained.

## Acquire a machine token from the CLI

From the installation shown in the installation guide, run the interactive
command:

```sh
./vendor/m7-identity-cli-php-0.1.2/bin/m7-identity client-credentials --interactive
```

The client secret is read with terminal echo disabled. Environment values take
precedence over command options, and options are fallback values only:

```text
defined environment variable > matching CLI option > selected profile > prompt/default
```

For the client password, an explicit hidden `--password` prompt sits above the
stored profile password and below environment/command-file sources.

When `--app` is selected, the complete successful response is stored under
that app's selected client-credentials profile before stdout. This also
applies to `--output token`; only the display is shortened. The previous
active generation moves to history. Calls using raw client-ID configuration
without an app remain stateless, and an overridden client ID cannot differ
from a selected app.

For an unattended process, prefer a mode-0600 secret file:

```sh
export M7_CLIENT_CREDENTIALS_CLIENT_ID='YOUR_REGISTERED_CLIENT_ID'
export M7_CLIENT_CREDENTIALS_CLIENT_SECRET_FILE='/run/secrets/m7-client-secret'
export M7_CLIENT_CREDENTIALS_AUD='https://api.user.m7.org'

./vendor/m7-identity-cli-php-0.1.2/bin/m7-identity client-credentials --output token
```

There is deliberately no plaintext `--client-secret` option. Run the command
with `--help` for every mapping and its exact precedence and file rules. The
sections below cover managed setup, secrets, storage, named profiles, stateless
calls, library use, and complete copy/paste workflows.

## Authorize a user device from the CLI

For a registered public device client, upsert a native app and device-code
profile, then run it by local app name:

```sh
./vendor/m7-identity-cli-php-0.1.2/bin/m7-identity app upsert user.m7.org-device \
  --client-id YOUR_REGISTERED_DEVICE_CLIENT_ID \
  --type native \
  --authority consumer

./vendor/m7-identity-cli-php-0.1.2/bin/m7-identity profile upsert \
  --app user.m7.org-device \
  --grant device-code \
  --config-json '{"auth_method":"none","aud":["https://user.m7.org"],"scope":"openid profile offline_access","refresh_mode":"strict_rotation"}'

./vendor/m7-identity-cli-php-0.1.2/bin/m7-identity device-code \
  --app user.m7.org-device
```

These setup commands are repeatable. Upsert creates missing records or replaces
their complete editable definitions without merging old fields. Stored
passwords, active/pending token generations, history, and creation timestamps
are preserved.

The complete successful bundle is committed to the selected profile before
stdout. Machine apps are rejected for device code. The raw stateless form is:

```sh
export M7_DEVICE_CODE_AUTH_METHOD='none'
export M7_DEVICE_CODE_CLIENT_ID='YOUR_REGISTERED_DEVICE_CLIENT_ID'
export M7_DEVICE_CODE_SCOPE='openid profile offline_access'

./vendor/m7-identity-cli-php-0.1.2/bin/m7-identity device-code
```

The command displays the authority approval URL and user code, then polls at
the returned interval. Approval and account-password entry happen only in the
browser at M7; the CLI never collects the password. Basic/Post confidential
device clients use the same protected client-secret source as client
credentials.

The following sections cover registration, acquisition, storage verification,
strict rotation, regular ACK, fault-tolerant ACK supersession, protected
resume/import, DPoP boundaries, and direct library use.

Use `--start-only` to return the authorization JSON without polling. That JSON
contains a sensitive `device_code`. Resume through
`M7_DEVICE_CODE_DEVICE_CODE_FILE` or `--device-code-file`, using a regular
non-symlink file with no group/other permissions. There is no plaintext
device-code argument.

## Exchange the returned refresh package

Managed strict rotation reads and atomically replaces the active bundle:

```sh
M7_EXCHANGE_REFRESH_MODE=strict_rotation \
  ./vendor/m7-identity-cli-php-0.1.2/bin/m7-identity exchange \
    --app user.m7.org-device
```

Managed ACK rotation retains the provisional successor as pending and leaves
active unchanged until the matching ACK promotes it:

```sh
M7_EXCHANGE_REFRESH_MODE=ack \
  ./vendor/m7-identity-cli-php-0.1.2/bin/m7-identity exchange \
    --app user.m7.org-device

./vendor/m7-identity-cli-php-0.1.2/bin/m7-identity ack \
  --app user.m7.org-device
```

Use `--id GENERATION` when multiple pending generations make selection
ambiguous. `M7_EXCHANGE_REFRESH_MODE=ack_supersede_pending` can begin from the
active package and replaces any older pending child at Identity. If the old
pending generation is retained locally, exchange stores the new generation
before removing it. A failed exchange or ACK leaves recoverable local state
unchanged.

For stateless operation, the device result is already the bundle used for
refresh. Protect its refresh token and binding values as one unit:

```sh
chmod 0600 /run/secrets/m7-refresh-bundle.json
export M7_EXCHANGE_AUTH_METHOD='none'
export M7_EXCHANGE_CLIENT_ID='YOUR_REGISTERED_DEVICE_CLIENT_ID'

./vendor/m7-identity-cli-php-0.1.2/bin/m7-identity exchange \
  --bundle-file /run/secrets/m7-refresh-bundle.json
```

Use `--bundle-stdin` for explicitly piped JSON, or `--interactive` to select a
protected file or enter the three required refresh fields with terminal echo
disabled. No refresh or binding value is accepted as a command argument.

The stateless successful output is the complete replacement bundle; the caller
must persist it safely before retiring the predecessor. A boolean bundle
`fingerprint: true` means a binding exists but is not the fingerprint value;
supply that protected value separately. Stateless ACK and supersede continue
to accept protected bundle files or explicitly selected stdin.
The native client registration must authorize `refresh_token` as well as the
standard device-code grant URN.
Redirect output only to a different protected file, never onto the current
input path, because shell redirection truncates the destination first.

## Reuse or renew a managed token

After client credentials or device code has established a profile, use the
managed ensure command for ordinary token consumption:

```sh
./vendor/m7-identity-cli-php-0.1.2/bin/m7-identity ensure \
  --app user.m7.org-cc

./vendor/m7-identity-cli-php-0.1.2/bin/m7-identity ensure \
  --app user.m7.org-device \
  --minimum-ttl 300
```

The default output is the usable access token. Internally, a fresh bundle is
reused without a write; a stale machine token runs client credentials, and a
stale native token exchanges the complete refresh package. Machine profiles
can bootstrap without a bundle. Native profiles cannot: run `device-code`
first. Ensure never starts browser authorization automatically.

ACK mode durably stores pending state before acknowledgement and promotes only
after success. A failed ACK remains recoverable; `--id` selects among pending
generations. Regular `ack` serializes one pending child, while
`ack_supersede_pending` lets Identity replace an older pending child when
refreshing from the retained active package.

PHP integrations call `ensureAccessToken()` directly and persist
`report->bundle()` in their own keychain, database, or secrets store. The
library accepts null only for `client_credentials`; `exchange` requires a
device-primed bundle. Its result statuses and ACK checkpoint behavior follow
the storage-neutral contract described here.

The library orchestrator accepts caller-owned current state and returns the
complete resulting bundle without selecting storage. For ACK modes, it calls a
caller-owned durable checkpoint with the pending successor before sending ACK;
without that checkpoint it returns `pending_ack` and does not acknowledge.
`m7-identity ensure` is the separate adapter that resolves the app and profile,
loads keychain state, supplies that checkpoint, and atomically stores or
promotes replacements.

## Send an authenticated resource request

Ensure and use the bundle stored beneath a machine or native app:

```sh
./vendor/m7-identity-cli-php-0.1.2/bin/m7-identity request \
  --app user.m7.org-device \
  --url https://api.user.m7.org/api/v2/session/me \
  --response json \
  --query include=groups
```

The profile defaults to `default`; the app type selects client-credentials
acquisition or device-bundle exchange. A token with at least 60 seconds left is
reused. Otherwise the replacement is stored before the API request. Use
`--minimum-ttl` to change that threshold and `--refresh-mode` to override a
native profile's rotation mode. A profile without a configured mode uses
`ack_supersede_pending`. Native apps must first be primed with `device-code`.

The method defaults to GET without JSON and automatically becomes POST when
`--json` or `--json-file` supplies data. An explicit method wins. Requests
default to JSON Accept, and body-bearing requests default to JSON Content-Type.
Repeat `--query` and `--header` for ordinary request inputs.

`--response raw` is the default and emits the response body without JSON
validation. Use `--response json` when JSON is required. A malformed, empty, or
incorrectly typed 2xx response then exits `1`; its original body is still
written to stdout and the validation reason is written to stderr.

The underlying `ResourceRequestReport` always exposes the raw `body()`, HTTP
status, and response headers. In JSON mode, `hasJson()` distinguishes a decoded
JSON `null` from no decoded value, `json()` returns the decoded representation,
and `toArray()` carries both raw and decoded views. A missing or non-JSON
response media type, or malformed JSON under a JSON media type, fails the
report even when HTTP returned 2xx; the raw body remains available for
diagnosis.

For stateless use, select a protected complete bundle with `--bundle PATH` or
`--bundle-stdin`, or use a mode-0600 access-token file or the value-less hidden
`--token` prompt. A fingerprint and fresh caller-created DPoP proof likewise
come from protected files or hidden prompts. Refresh-only `binding_chain` and
`binding_link` values are never sent to the API.

Explicit bundle and manual-token sources are literal: they are not refreshed
or stored. After managed preflight, the command sends exactly one bounded HTTPS
request. It does not follow redirects, generate a DPoP proof, or blindly retry
an API `401`. The request lifecycle above applies to every input source and
environment override.

## Acquire a machine token from PHP

Use the library operation when a confidential application needs to act as
itself:

```php
$identity = new M7\Identity\M7IdentitySDK();
$report = $identity->clientCredentials([
    'endpoint' => 'consumer',
    'auth' => [
        'method' => 'client_secret_basic',
        'client_id' => $clientId,
        'client_secret' => $clientSecret,
    ],
    'aud' => 'https://api.user.m7.org',
    'scope' => 'profile.read',
    'access_expires' => 900,
]);

if (!$report->ok()) {
    throw new RuntimeException($report->reason());
}

$accessToken = $report->accessToken();
```

`client_secret_post` is also supported. Public-client method `none` is not,
because this grant represents a confidential application. A returned token has
been acquired, not validated; call `validate()` or `introspect()` separately
when that assurance is required.

The library itself does not read environment variables, prompt for secrets, or
print the token. Those behaviors stay in `cli-php`.

## Requirements

Both PHP packages require PHP 8.1 or newer and OpenSSL. cURL is recommended for
token acquisition, authenticated resource requests, live certificate
resolution, introspection, and UserInfo; supported PHP streams are the
fallback transport.

## Install

Follow the [CLI and background-process installation guide](https://m7.org/docs/sdk/m7-identity/cli/installation.md) for
the published library and CLI packages, and the separate source-checkout path
for development use.

## Operation model

Use `validate()` when the process has a serialized token and must verify it
locally against explicit policy. Use `clientCredentials()` when a confidential
machine client needs an application-principal access token. Use
`deviceAuthorization()` plus repeated `deviceToken()` attempts when a person
must approve a public or confidential device client. Use `request()` to call
an application resource with a complete active token bundle. Use
`introspect()` when current authority state is required. Use `userinfo()` when
the process is authorized to retrieve the live scoped identity projection.

Keep these decisions separate:

| Operation | A successful result means |
| --- | --- |
| `validate()` | The supplied token passed the configured local cryptographic and claims policy. |
| `clientCredentials()` | The configured authority returned a well-formed access-token-only client-credentials envelope. The token has not thereby passed local validation or live introspection. |
| `tokenExchange()` | One original user access token produced a well-formed access-only or offline bundle for an approved target. It did not validate JWT signatures, refresh, retry or change storage. |
| `deviceAuthorization()` | The authority returned a validated device code, user code, verification URL, expiry, and polling interval. No token has been issued to the caller yet. |
| `deviceToken()` | One poll returned either a validated user token bundle or a classified pending/slow-down/terminal report. It does not sleep or validate returned JWTs cryptographically. |
| `refreshToken()` | One complete predecessor refresh package produced a validated replacement package. The operation did not persist or cryptographically validate its JWTs. |
| `acknowledgeToken()` | One retained pending package produced a matching active successor without changing caller storage. |
| `request()` | One bounded authenticated HTTPS resource request returned a 2xx response and, when requested, a valid JSON representation. It did not refresh, retry, interpret the API schema, or change storage. |
| `introspect()` | The authority returned a valid introspection response; `active()` is true only when it explicitly declared the token active. |
| `introspectAcl()` | M7 returned a valid receiving-list machine ACL decision; `allowed()` is true only for an active token and active ACL result. The resource must still verify DPoP or mTLS proof. |
| `userinfo()` | The protected resource returned a valid identity claims object with a nonempty subject, plus any requested subject binding passed. |

`introspectAcl()` is included in the verified Token/PHP 0.1.4 ZIP. A receiving
PHP service should follow the
[inbound machine access guide](https://m7.org/docs/sdk/m7-identity/website/inbound-machine-access.md).

For a personal admin owner proxy, SSO returns the owner's `principal()` plus
`value('owner_proxy')`. The SDK retains that binding without changing token
claims or mapping its machine subject to a user. Direct consumers must
validate and interpret it; updated shared M7Roster performs that mapping.
Both proxy and owner must already be active, unexpired members of the same
receiver list. See [owner-proxy handling](https://m7.org/docs/sdk/m7-identity/website/inbound-machine-access.md#personal-admin-owner-proxies).

A transport failure, malformed response, authorization failure, or explicit
inactive result fails the corresponding live operation closed.

## Security

Treat parsed claims as untrusted until validation succeeds. An unvalidated
issuer or key identifier may help select a candidate certificate cache entry,
but it must never establish trust by itself.

When DPoP validation is enabled, the consuming process owns atomic replay-key
claiming and retention. A successful SDK proof result identifies the replay key
and expiry boundary; it does not reserve the key in application storage.

## Organization and email acquisition

- [Consumer organizations, live membership facts and service authorization](https://m7.org/docs/sdk/m7-identity/cli/organization-access.md)
- [API User account email list and exact subject validation](https://m7.org/docs/sdk/m7-identity/cli/account-emails.md)

These Token/PHP operations are included in the verified 0.1.4 ZIP. Install the
complete package with all org/email classes and facade
wiring. Existing released capabilities and download integrity guidance remain
separate. No org/email executable commands or automatic session ownership are
implied by these library operations.
