Files
hncb-fusion-deid-demo/fusion/POLICY_SETUP.md
Conan Scott b5de55d4a6 feat(net): custom domain for the Fusion-facing API
Fusion couldn't resolve the default *.execute-api.amazonaws.com endpoint, so front
the same HTTP API with hncb-deid.apim-apac-demo.com (ACM DNS-validated cert,
apigatewayv2 REGIONAL custom domain, root api mapping, Route53 A ALIAS). Callers
resolve our hostname straight to IPs and never touch an execute-api name; also
survives API-id churn. Point POLICY_SETUP + README demo at the custom URLs.

Verified live: resolves to IPs, 401 unauthenticated, tokenizes with the key.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 11:04:19 +10:00

96 lines
5.4 KiB
Markdown

# Fusion (shared SaaS) policy setup — de-identification as a gateway policy
Fusion (Axway Amplify AI Gateway) is a **shared SaaS instance**, configured in the
console — not deployed by this repo. Here it is the **enforcement plane** in front
of the AgentCore agent: de-identification is a **policy on the route**, not
application code. `/tokenize` and `/restore` are the policy's **transform backend**
(they hold the detector + vault on-prem); Fusion applies them inline to traffic.
> **Why put Fusion in the path at all?** Functionally, everything below is an HTTP
> call — the `/demo` orchestrator proves a plain client can do the same mechanics.
> That is the point: the mechanics are **not** the differentiator. Fusion adds what
> a plain call cannot:
> - **Non-bypassable enforcement** — de-id is applied at the gateway to *all* AI
> traffic, not trusted to each app to call `/tokenize` correctly.
> - **Central audit** — the gateway's own trace is the compliance artifact proving
> only tokens ever reached the model. (See the money-shot check in the README.)
> - **Reuse** — one policy governs every model/agent/app behind Fusion; routing,
> rate-limits, and guardrails come with it.
>
> Drop Fusion and you lose *enforcement and audit*, not capability.
## The policy model
Fusion fronts the AgentCore Runtime as a governed route. Two policies wrap it:
```
advisor ─▶ [ Fusion route to the agent ]
│ request policy (ingress) ── tokenize transform ─▶ /tokenize
AgentCore Runtime (sees TOKENS only)
│ response policy (egress) ── restore transform ──▶ /restore
advisor ◀────┘
```
The transforms run **inside the policy boundary**; the raw name never crosses it.
## Backend the policy calls
Use the **custom domain** — Fusion could not resolve the default
`*.execute-api.ap-southeast-1.amazonaws.com` endpoint, so the API is fronted by a
Route53-aliased name that resolves straight to IPs (see `terraform/custom_domain.tf`).
- `TOKENIZE_URL` = `https://hncb-deid.apim-apac-demo.com/tokenize`
`terraform -chdir=terraform output -raw tokenize_url_custom`
- `RESTORE_URL` = `https://hncb-deid.apim-apac-demo.com/restore`
`terraform -chdir=terraform output -raw restore_url_custom`
- `AGENT_RUNTIME_ARN` (the route's upstream) = printed by `scripts/agentcore_setup.sh`
- **Auth:** public API Gateway HTTP API; send the shared secret in the **`x-api-key`**
header (`Authorization: Bearer <secret>` also works). The secret is the Terraform
`tokenize_api_key` var (gitignored `terraform/local.auto.tfvars`, never committed) —
hand it to the console operator out of band.
- *Note:* the raw `…execute-api…` URLs (`output -raw tokenize_url` / `restore_url`)
still work for anything that can resolve them; Fusion should use the custom domain.
- *Note:* a Lambda Function URL was the first choice, but this account's SCP blocks
unauthenticated Function URLs, so the public front door is API Gateway.
## Request policy — ingress (prompt bound for the agent)
Applied to every request on the route; the app cannot skip it.
1. **AuthN / RBAC / business-purpose** check (Fusion native).
2. **Tokenize transform:** `POST { query }``TOKENIZE_URL` → `{ deidentified_prompt,
session_id }`. The endpoint runs detect → mint → vault-write → splice (typed
findings, never a redacted blob). **Replace** the outbound prompt with
`deidentified_prompt`; carry `session_id` as policy/session context.
3. **Forward** to the AgentCore upstream with `{ "prompt": deidentified_prompt }` —
tokens only.
4. **Audit:** record the **tokenized** payload; the raw query never leaves the policy.
## Response policy — egress (agent's answer)
Applied to every response on the route.
1. **Restore transform:** `POST { session_id, text }` → `RESTORE_URL` → `{ final }`
(vault lookup scoped to this session; re-attaches identity at the envelope and
swaps any inline tokens).
2. **Return** `{ "final": ..., "deidentified_prompt": ... }` so the UI renders the
restored-vs-tokenized split view.
## Vault item shape (DynamoDB, written by the tokenize transform)
One row per detected entity. `value` is the original PII (so restore can put it
back); a resolvable PERSON also gets a `customer_id` so the RAG tool can turn the
token into a de-identified evidence package.
```
{ "token": "CUST_863651", "type": "PERSON", "value": "王小明",
"session_id": "<session>", "expires_at": <epoch+ttl>, "customer_id": "cust-0001" }
{ "token": "TW_683250", "type": "TW_ROC_ID", "value": "A123456789",
"session_id": "<session>", "expires_at": <epoch+ttl> }
```
(The seed's `--with-demo-token` writes a different `type=CUSTOMER, value=cust-0001`
row; that's only a standalone RAG test aid, not what the tokenize transform mints.)
## Fusion vs. the `/demo` orchestrator (be honest on camera)
`gateway_api/orchestrator/` (`/demo`) runs the *identical* tokenize → agent →
restore mechanics over plain HTTP, so the Fusion-less demo works end to end. Show
that to prove the data-flow. Then make the governance point: only Fusion turns
those mechanics into a **mandatory, audited policy** across all AI traffic — the
enforcement boundary an app-level HTTP call can't guarantee.
> Not automated here: Fusion is shared SaaS and configured by a human in the
> console. This file is the spec for that configuration.