diff --git a/TECHNICAL.md b/TECHNICAL.md
index 1dcab2a..95ec59a 100644
--- a/TECHNICAL.md
+++ b/TECHNICAL.md
@@ -13,7 +13,11 @@ and managed-identity access to credentials.
## Single-region architecture
-
+
+
+*Component overview, not a delivery timeline. The numbered delivery arrow does not mean
+the Function waits for handset receipt; see the [request sequence](docs/BRING-YOUR-OWN-PROVIDER.md#end-to-end-endpoint-request)
+for live SMS response ordering and the end user's verification steps.*
Microsoft Entra ID sends an authenticated, encrypted request. Easy Auth checks the caller,
then the Function decrypts the request using its Key Vault-backed key. For a live request, the
@@ -25,6 +29,8 @@ with capacity and quota for your hosting plan. The provider-secret path applies
integrations; OAuth integrations use managed-identity token exchange instead. Credential caching
depends on the [selected cache settings](docs/CONTRACT.md#credential-caching-and-refresh), and
[log formats vary by runtime](docs/MONITORING.md#runtime-specific-log-discovery).
+Telemetry comes from the Function host/worker; the schematic telemetry arrow does not mean
+Easy Auth rejections produce Function request logs.
An authorized, valid encrypted **evaluation request (`mode: 2`)** returns the matching nonce
without submitting a message to the provider. When caching is enabled, credential preparation
diff --git a/docs/BRING-YOUR-OWN-PROVIDER.md b/docs/BRING-YOUR-OWN-PROVIDER.md
index 7f6fb69..68bef93 100644
--- a/docs/BRING-YOUR-OWN-PROVIDER.md
+++ b/docs/BRING-YOUR-OWN-PROVIDER.md
@@ -9,6 +9,37 @@ Before deploying, ask your tenant administrator to confirm
[eligibility, testing, and activation with Microsoft](ONBOARDING.md#before-purchasing-or-deploying).
Deploying the endpoint alone does not enable tenant policy.
+## End-to-end endpoint request
+
+This is a browser-based **SMS sign-in example after approved endpoint activation**. Your
+application is the user's existing sign-in app, **not** the dedicated endpoint app registration
+created during setup. Do not add a redirect URI to that endpoint registration to copy this flow.
+
+
+
+*Original UML sequence; [open the scalable diagram](images/request-sequence.svg).
+Solid arrows are requests/delivery; dashed arrows are replies. The `par` frame separates
+provider acceptance from recipient delivery; it does not promise their order or successful delivery.
+This is a successful example, not a completed live test. Use the
+[endpoint error reference](CONTRACT.md#epp-response-json) for rejected or failed endpoint requests.*
+
+The person reads the SMS on their phone and enters its code in the **Microsoft Entra ID sign-in
+page**, as described in Microsoft's [text-message verification instructions](https://learn.microsoft.com/en-us/entra/identity/authentication/concept-authentication-phone-options#text-message-verification).
+Microsoft Entra ID checks the code and remaining sign-in requirements; the application validates
+the sign-in result and applies its access rules. The
+[browser redirect/token exchange](https://learn.microsoft.com/en-us/entra/identity-platform/v2-protocols-oidc#protocol-flow-sign-in)
+belongs to that application, not this Function.
+
+The Function only delivers the supplied message. For live requests, its `200` response means provider acceptance,
+**not phone receipt, a correct user-entered code, or application access**. Evaluation (`mode: 2`)
+tests the endpoint without sending a message; it is not an end-user sign-in.
+Voice and other sign-in flows can have different prompts; confirm their experience through the
+[approved validation procedure](ONBOARDING.md#validate-the-deployed-endpoint), rather than assuming
+they use this SMS screen or a generic voice-MFA gesture.
+
+The steps below explain the endpoint's part of the sequence. The
+[architecture view](../TECHNICAL.md#single-region-architecture) shows its separate key and credential paths.
+
## 1. Verify the caller
@@ -188,7 +219,8 @@ or send-retry loop. Keep service secrets in Key Vault, not in settings or source
3. Add tests in the directory above for registration, request mapping, missing credentials,
recognized acceptance, rejection, unknown responses, and timeouts on each supported channel.
Check that evaluation sends nothing, live success waits for acceptance, and failures omit the
- nonce. Use fake credentials and service responses; run your runtime's tests and build.
+ nonce. Use fake credentials and service responses; [run the offline tests](#run-the-offline-tests)
+ before building your package.
4. [Package your changed source](../TECHNICAL.md#build-zips-locally), not an unchanged public
release. C# needs publish output; Python needs the documented Linux remote build.
Follow [manual deployment](ONBOARDING.md#optional-local-development-and-manual-deployment)
@@ -196,6 +228,26 @@ or send-retry loop. Keep service secrets in Key Vault, not in settings or source
Retain the [Easy Auth trust checks](ONBOARDING.md#manual-deployment-trust-checklist), then
complete the validation below.
+### Run the offline tests
+
+Use a **full repository checkout**, not a downloaded Function release ZIP: the ZIPs omit tests,
+and the suites use the shared `tests/fixtures/contract.json`. Run the unchanged suite first,
+then rerun it after adding your integration. You do not need a tenant, app registration,
+`local.settings.json`, Azurite, Core Tools, or provider account for these tests.
+
+Choose only your runtime's instructions:
+[JavaScript](../javascript/README.md#run-offline-tests),
+[C#](../dotnet/README.md#run-offline-tests), or
+[Python](../python/README.md#run-offline-tests). Each gives the working folder, tools, and commands.
+Use a terminal without production or deployed provider settings in its environment.
+Dependency preparation needs the package registry; the tests use local test keys and
+fake credentials/transport rather than Azure sign-in or live sends.
+
+Use the test runner's final summary and exit code: no collected tests, a missing module, or a
+failed restore is not a passing run. These commands follow the repository's
+[CI workflow](../.github/workflows/ci.yml). A pass checks the application, not deployed Easy Auth,
+provider authorization, or delivery; continue with the separate acceptance checks below.
+
diff --git a/docs/FRONTDOOR.md b/docs/FRONTDOOR.md
index f6020ab..d146062 100644
--- a/docs/FRONTDOOR.md
+++ b/docs/FRONTDOOR.md
@@ -44,7 +44,10 @@ approved by the deployment owners; the observed results below do not establish t
## Architecture: one URL, multiple origins
-
+
+
+*Component overview, not a deployment template or delivery timeline. Match private-key material,
+not vault version IDs or resource names. The green dashed arrows are responses, not health probes.*
The diagram shows the design, not the exact test deployment. Its region names are examples.
Custom domains and Premium/WAF are optional and weren't part of the Standard-tier test.
@@ -54,6 +57,10 @@ consistent and use the same RSA key**, rather than copying every setting literal
The SMS/voice arrows show live delivery. Evaluation returns the nonce without contacting the provider.
The diagram's provider-secret arrows apply to API-key integrations; OAuth uses each region's
outbound managed identity. Match the key material across regions, not the vault URLs or version IDs.
+For response timing and separate recipient delivery, see the
+[request sequence](BRING-YOUR-OWN-PROVIDER.md#end-to-end-endpoint-request).
+Health probes require the [separate readiness handler](#3-provide-a-non-delivering-readiness-endpoint),
+which is not shown in this overview or included in the release packages.
| Component | Purpose |
|---|---|
diff --git a/docs/ONBOARDING.md b/docs/ONBOARDING.md
index d63ebd6..00f51e8 100644
--- a/docs/ONBOARDING.md
+++ b/docs/ONBOARDING.md
@@ -98,10 +98,29 @@ Review the deployment plan, then retain `deployment-*.json` and the public certi
The [setup output guide](../setup/docs/README.md#read-the-deployment-summary) explains the remaining
fields. `policyChanged: false` is expected, not a failed deployment.
-Inspect cloud settings in **Azure portal > Function App > Settings > Environment variables**.
-They are setup-managed; do not recreate them from the local sample or bulk-copy another app's
-settings. Do not export all settings into a ticket. Changes can restart the worker, and reruns
-can restore setup-managed values.
+To find your endpoint's cloud settings:
+
+1. In **Azure portal > Function Apps**, open the name in `resources.functionApp` in your saved
+ deployment summary. Check its subscription and resource group against `subscriptionId` and
+ `resources.resourceGroup`; do not select an app just because its display name looks similar.
+2. Open **Settings > Environment variables > App settings**. Use the **Search** box to find
+ `EPP_PROVIDER_NAME`, `EPP_PROVIDER_SMS_ENDPOINT`, or `EPP_PROVIDER_VOICE_ENDPOINT`, then use
+ the table below to compare the approved routing configuration privately.
+3. Find `EPP_DECRYPTION_KEY_PEM` by **Name**, but keep its value hidden. It is the private-key
+ reference, not a provider API key. Do not select **Show values** to export settings or include
+ them in a screenshot. Use the [key-reference checks](../setup/docs/Troubleshooting.md#certificate-selection-reuse-or-cleanup-fails)
+ if resolution fails.
+
+[](images/onboarding/function-app-settings.png)
+
+*Navigation example from [Microsoft's Function settings instructions](https://learn.microsoft.com/en-us/azure/azure-functions/functions-how-to-use-azure-function-app-settings#get-started-in-the-azure-portal).
+`function-app-1` is a demo name; use `resources.functionApp`. The generic host settings shown
+are not an EPP checklist, and FC1/EP1 settings differ.
+[Unmodified source](https://github.com/MicrosoftDocs/azure-docs/blob/1067858212cfae5f69de14fe53a3432eb461b052/articles/azure-functions/media/functions-how-to-use-azure-function-app-settings/azure-function-app-main.png),
+Copyright (c) Microsoft Corporation, [CC BY 4.0](https://github.com/MicrosoftDocs/azure-docs/blob/1067858212cfae5f69de14fe53a3432eb461b052/LICENSE).*
+
+These settings are setup-managed; do not recreate them from the local sample or bulk-copy another
+app's settings. Changes can restart the worker, and reruns can restore setup-managed values.
| Value or setting | Source / verification | Customer action |
|---|---|---|
@@ -124,7 +143,9 @@ a missing, empty, or invalid selected endpoint fails closed.
There are **three separate trust paths**: Easy Auth admits the Microsoft caller; the Key Vault
certificate decrypts its JWE; provider credentials authorize outbound delivery. The HTTPS
-certificate is separate again. Fix the failing path rather than replacing unrelated IDs or keys.
+certificate is separate again. Use the [request sequence](BRING-YOUR-OWN-PROVIDER.md#end-to-end-endpoint-request)
+to distinguish provider acceptance, phone receipt, and the user's code verification. Fix the failing path rather than
+replacing unrelated IDs or keys.
## Complete provider authentication
diff --git a/docs/images/onboarding/certificate-version.png b/docs/images/onboarding/certificate-version.png
new file mode 100644
index 0000000..068a21f
Binary files /dev/null and b/docs/images/onboarding/certificate-version.png differ
diff --git a/docs/images/onboarding/function-app-settings.png b/docs/images/onboarding/function-app-settings.png
new file mode 100644
index 0000000..6031c3b
Binary files /dev/null and b/docs/images/onboarding/function-app-settings.png differ
diff --git a/docs/images/request-sequence.png b/docs/images/request-sequence.png
new file mode 100644
index 0000000..4914f99
Binary files /dev/null and b/docs/images/request-sequence.png differ
diff --git a/docs/images/request-sequence.svg b/docs/images/request-sequence.svg
new file mode 100644
index 0000000..9b1cdc4
--- /dev/null
+++ b/docs/images/request-sequence.svg
@@ -0,0 +1,88 @@
+
diff --git a/dotnet/README.md b/dotnet/README.md
index 5f647df..8e5419c 100644
--- a/dotnet/README.md
+++ b/dotnet/README.md
@@ -40,13 +40,27 @@ used JavaScript; validate an equivalent .NET readiness implementation and deploy
before `SendOtp` runs. Those failures therefore do not produce application `ILogger` events or the
handler's custom error response body; platform diagnostics and responses apply instead.
4. Build [dotnet.csproj](dotnet.csproj), run the offline xUnit suites in
- [tests/Epp.Otp.Tests.csproj](tests/Epp.Otp.Tests.csproj), and start the local Functions host from this folder.
+ [tests/Epp.Otp.Tests.csproj](tests/Epp.Otp.Tests.csproj) using the
+ [offline test commands](#run-offline-tests), and start the local Functions host from this folder.
Core Tools has no Easy Auth: bind only to loopback, with no tunnels or public forwarding.
5. Publish this app folder to a compatible .NET isolated Function App. Inspect the package: both
[.funcignore](.funcignore) and the project protect local settings; private keys must not be included.
Offline tests cover application behavior, not platform authentication; run the separate
[deployed security checks](../docs/ONBOARDING.md#4-package-deploy-and-verify).
+## Run offline tests
+
+For developer work, open `dotnet/` in a full repository checkout with the **.NET 8 SDK**.
+Follow the [shared offline prerequisites](../docs/BRING-YOUR-OWN-PROVIDER.md#run-the-offline-tests);
+you do not need to start the Functions host or configure a provider.
+
+```powershell
+dotnet test .\tests\Epp.Otp.Tests.csproj
+```
+
+`dotnet test` restores and builds the test project, which compiles the handler and shared source
+directly. Also run `dotnet build` in `dotnet/` before packaging to check the actual Function project.
+
## Environment configuration
Run Core Tools from `dotnet/`. Create an untracked `local.settings.json` beside
diff --git a/javascript/README.md b/javascript/README.md
index 460301f..accd126 100644
--- a/javascript/README.md
+++ b/javascript/README.md
@@ -36,13 +36,26 @@ sample's release package. No Front Door deployment script is supplied.
trusted tenant issuer, endpoint-app `allowedAudiences` and a nonempty `allowedApplications` list for
the authorized Microsoft caller. Do not exclude SendOtp. There is no backup application token validation;
never expose the endpoint to the public internet with Easy Auth disabled or bypassed.
-5. Install dependencies from [package.json](package.json), run its offline test script and start the
+5. Install dependencies from [package.json](package.json), run the
+ [offline test commands](#run-offline-tests), and start the
local Functions host from this folder. Core Tools has no Easy Auth: bind only to loopback, with no
tunnels or public forwarding. Publish this app folder only, preserving dependencies and observing
[.funcignore](.funcignore); inspect the package before upload. Offline tests cover application
behavior, not platform authentication; run the separate
[deployed security checks](../docs/ONBOARDING.md#4-package-deploy-and-verify).
+## Run offline tests
+
+For developer work, open `javascript/` in a full repository checkout with **Node.js 20 or 22
+and npm**. Follow the [shared offline prerequisites](../docs/BRING-YOUR-OWN-PROVIDER.md#run-the-offline-tests);
+you do not need to start the Functions host or configure a provider.
+
+```powershell
+# First checkout or changed lockfile:
+npm ci
+npm test
+```
+
## Environment configuration
Run Core Tools from `javascript/`. Create an untracked `local.settings.json` **beside
diff --git a/python/README.md b/python/README.md
index 8a2e810..599dae2 100644
--- a/python/README.md
+++ b/python/README.md
@@ -35,13 +35,27 @@ used JavaScript; validate an equivalent Python readiness implementation and depl
caller. Do not exclude SendOtp. There is no backup application token validation; never expose the
endpoint to the public internet with Easy Auth disabled or bypassed.
4. Use a virtual environment, install [requirements.txt](requirements.txt) and pytest, then run the
- offline [tests/](tests/) from this folder. Start the local Functions host from this app root.
+ offline [tests/](tests/) from this folder using the
+ [offline test commands](#run-offline-tests). Start the local Functions host from this app root.
Core Tools has no Easy Auth: bind only to loopback, with no tunnels or public forwarding.
5. Publish this folder to a compatible Linux Python Function App with dependencies or a supported
remote build. Inspect the package and apply [.funcignore](.funcignore); keep local settings and keys private.
Offline tests cover application behavior, not platform authentication; run the separate
[deployed security checks](../docs/ONBOARDING.md#4-package-deploy-and-verify).
+## Run offline tests
+
+For developer work, open `python/` in a full repository checkout with **Python 3.11 in an
+activated virtual environment**. Follow the
+[shared offline prerequisites](../docs/BRING-YOUR-OWN-PROVIDER.md#run-the-offline-tests);
+you do not need to start the Functions host or configure a provider.
+
+```powershell
+# First checkout or changed requirements:
+python -m pip install -r requirements.txt pytest
+python -m pytest tests
+```
+
## Environment configuration
Run Core Tools from `python/`. Create an untracked `local.settings.json` beside
diff --git a/setup/docs/README.md b/setup/docs/README.md
index 2e74f09..d093e8c 100644
--- a/setup/docs/README.md
+++ b/setup/docs/README.md
@@ -302,12 +302,34 @@ Interactive selection never defaults to the newest certificate or to cleanup. En
stops before deployment; invalid locations can be corrected or followed by another choice.
Selecting an action does not apply it: the complete deployment approval lists the exact removals.
-For reuse, open **Azure portal > Key Vault > Certificates > certificate > version** and copy
-**Certificate Identifier**, of the form
-`https://.vault.azure.net/certificates//`. A version is required.
-Do not supply a vault-only URL, `/keys/` URL, public `.cer` file, secret value, or a versionless
-reference. Setup reads the public certificate and resolves the corresponding **versioned backing
-secret**; the certificate and secret version identifiers need not be identical.
+**Reuse path only:** choose **Reuse** interactively or pass `-CertificateAction Reuse`. Unattended
+reuse requires both `-EncryptionKeyId` and `-KeyVaultCertificateId`.
+
+1. Select the application's encryption credential key ID from setup's list or the app manifest's
+ `keyCredentials[].keyId`. This is the saved `encryptionKeyId`, not the application client ID.
+2. In **Azure portal > Key vaults**, open the certificate's source vault. The saved `certificateId`
+ identifies its vault, certificate name, and version; this can differ from the provider-credential
+ vault in `resources.keyVault`. Open **Objects > Certificates**, select that certificate, then
+ select the exact version matching the app credential, not automatically **Current version**.
+ Without a saved summary, ask the certificate owner for the vault and version matching the
+ selected public thumbprint.
+3. On **Certificate Version**, copy **Certificate Identifier** using the copy icon at the end of
+ that row. Use the complete HTTPS URL for `-KeyVaultCertificateId`; it must include the
+ `/certificates//` path. Do not copy **Key Identifier** or **Secret Identifier**,
+ and do not select **Download in PFX/PEM format**.
+
+[](../../docs/images/onboarding/certificate-version.png)
+
+*Field-location example from [Microsoft's certificate instructions](https://learn.microsoft.com/en-us/azure/key-vault/certificates/quick-create-portal#add-a-certificate-to-key-vault);
+do not follow its certificate-creation or private-key-export steps for this sample.
+The demo names, IDs, and expired dates are not reuse inputs; use your own valid, matching version.
+[Unmodified source](https://github.com/MicrosoftDocs/azure-docs/blob/22e0ade52c247e5d306a78dc9e392d901eb9c198/articles/key-vault/media/certificates/quick-create-portal/current-version-hidden.png),
+Copyright (c) Microsoft Corporation, [CC BY 4.0](https://github.com/MicrosoftDocs/azure-docs/blob/22e0ade52c247e5d306a78dc9e392d901eb9c198/LICENSE).*
+
+A version is required. Do not supply a vault-only URL, `/keys/` URL, public `.cer` file, secret value,
+or a versionless reference. Setup reads the public certificate and resolves the corresponding
+**versioned backing secret** for `EPP_DECRYPTION_KEY_PEM`; certificate and secret version identifiers
+need not be identical. The Function's private-key reference is an output, not a value to download.
The certificate must exactly match the selected registered certificate, not just its display
name or RSA key. It must use exportable RSA of at least 2048 bits with an enabled,