# Cross-Runtime Integration Demo · v1 **Owner:** Session A · A-owned runtime integration surface **Baseline:** `A-runtime-cross-integration-demo-v1` **Preview URL:** `/runtime/cross-runtime-integration/` **Planning rationale:** `/planning/cross-runtime-integration.html` ## Purpose Show **end-to-end** how a downstream runtime should consume BOTH existing A-owned services to make a gated decision: 1. **Policy first** — ask Admin Policy Engine whether this actor may touch this flag key in this context. 2. **Eval second** — only if policy says *allow*, ask Feature Flags Service to evaluate the flag. Before this demo the two services lived on their own. A reader could see each contract but had to imagine the wiring. This page is the wiring. ## Scope - Browser-side orchestration · `fetch` chain · no new backend process - 2 canonical test paths: **DENY** (sensitive flag · no approval → policy blocks) and **ALLOW** (either non-sensitive or approval-ref held → policy allows → flag eval runs) - Combined trace with `POLICY/` and `FLAGS/` prefixes so the operator sees both service traces interleaved - Honest `can_proceed_to_eval` + `flag_eval_status` signals - Deferred-items table rendered in the page itself (impossible to miss) ## Architecture relation ``` ┌────────────────────────────┐ ┌────────────────────────────┐ │ Admin Policy Engine (2a) │ │ Feature Flags Service (2a+)│ │ port 8090 │ │ port 8080 │ │ decision-only │ │ eval-only │ └─────────────┬──────────────┘ └──────────────┬─────────────┘ │ │ │ policy-first │ eval-second │ POST /api/policy/sensitive/check │ GET /api/flags/eval │ GET /api/policy/access/check │ │ │ └────────────┬──────────────────────────┘ │ ┌─────────▼──────────────┐ │ Cross-Runtime Demo │ │ (browser page · v1) │ │ /runtime/cross-runtime-│ │ integration/ │ └────────────────────────┘ ``` ## Input dependencies The demo expects these files / services to exist at runtime: | Dep | Where | Required? | |---|---|---| | Admin Policy Engine running on `http://127.0.0.1:8090` | `bash docs/runtime/admin-control-plane-service/run.sh` | **Yes** for policy panel | | Feature Flags Service running on `http://127.0.0.1:8080` | `bash docs/runtime/feature-flags-service/run.sh` | **Yes** for flag panel | | Registry file `/runtime/feature-flags/registry.json` | Static A-owned | **Yes** — drives sensitivity profile + dropdown | | Admin Phase 1 JSON models | `/runtime/admin-control-plane/*.json` | Loaded by policy service at startup | | B-owned `docs/kb/data/approval_matrix.json` | Read-only reference | Policy service references `row-sensitive-override` | If either service is not reachable, the page honestly surfaces a red banner. It does **not** fake success. ## Local / dev-only note **Not a production path.** - No JWT verification · dev-mode `X-PTT-*` headers or query params only - No audit sink · decisions emitted to each service's stdout only - No persistence · nothing stored server-side - No mutation · neither service writes state - Binds `127.0.0.1` only · not safe to expose publicly ## Non-goals 1. No new backend service — no Python app.py here. The orchestration is entirely in-browser. 2. No auth layer beyond what each service already accepts. 3. No mutation path — this is a read/decide demo, not a flip demo. 4. No Redis cache · no pubsub · no invalidation. 5. No admin Kanban · no approval CRUD. 6. No attempt to prove production readiness. ## Deferred items Reproduced in the page's "Honest limits" table: | Item | Status | Next logical phase | |---|---|---| | JWT / IdP verification | deferred | Admin 2b + FF 2b | | Approval store backing (Postgres) | deferred | Admin 2b | | Flag flip / transition API | deferred | FF 2c | | WORM audit sink (ptt.audit.trail) | deferred | Admin 2b+ | | Redis invalidation / pubsub | deferred | FF 2a+ infra | | Production auth | deferred | Phase 2b | | Admin mutation UI / Kanban | deferred | Admin 3 | | Real rollout persistence | deferred | FF 2c | | Server-side orchestrator service | not attempted | Gateway layer consolidation | ## Operator usage 1. **Start both services** (two terminals): ```bash bash docs/runtime/admin-control-plane-service/run.sh # port 8090 bash docs/runtime/feature-flags-service/run.sh # port 8080 ``` 2. **Open** `/runtime/cross-runtime-integration/` in a browser (via any static server, or the live deploy). 3. Click **"Check both services"** in the base-URL strip. Both pings should turn green. 4. Pick a **Preset** or fill the form manually: - `flag_key` drives the sensitivity profile + decides which policy endpoint the chain calls. - `actor_role` / `actor_tenant_id` / `target_tenant_id` / `target_user_id` populate both service contexts. - `approval_refs` lets you simulate "approval held" paths. - `now_iso` pins TTL math so outputs are reproducible. 5. Click **"Run policy-first · eval-second ▶"**. 6. Read the three result areas: - **Policy precheck panel** — endpoint hit · decision · reasons · required approvers · approval matrix row · policy trace. - **Flag eval panel** — only populated if policy said *allow* (or precheck was skipped). Shows `value`, `source`, `stage`, `bucket`, `evaluator_version`, flag trace. - **Combined result** — one-glance verdict + interleaved trace + deferred-items drawer. ## Deny path (CASE 1) Intended outcome: policy blocks, flag service is never called. Preset: **DENY · sensitive flag · no approval** - `flag_key = admin.control_plane_v1` (sensitive_flag=true, requires_approval=true) - `approval_refs` empty - Expect: `policy_gate_status = deny` · `can_proceed_to_eval = false` · `flag_eval_status = short_circuited_by_policy` - Reasons include `sensitive_dual_required` · `required_approvers = [tenant_dpo, platform_governance, domain_expert]` · `approval_matrix_row = row-sensitive-override` - Combined trace shows only `POLICY/…` lines followed by `FLAGS/ skipped…`. Preset: **DENY · cross-tenant no context** - `flag_key = cases.runtime_v1` (requires_approval, not sensitive) - `actor_role = sales_ae` with no `actor_tenant_id`, `target_tenant_id = pty-zeroth`, no assist_ctx / view_as_ctx - Expect: `policy_gate_status = deny` · reason `cross_tenant_no_context` · `tenant_relation = cross`. ## Allow path (CASE 2) Intended outcome: policy passes, flag eval runs. Preset: **ALLOW · non-sensitive flag** - `flag_key = flags.registry_v1` (non-sensitive, not requires_approval, stage=internal) - Expect: `policy_gate_status = skipped` (precheck not required) · `flag_eval_status = evaluated` - Policy panel shows "skipped". Flag panel shows `source=stage-internal`, `value=true` if tier is staff/admin. Preset: **ALLOW · sensitive + approval_ref** - `flag_key = admin.control_plane_v1` - `approval_refs = APP-row-sensitive-override-42` - Actor is own-tenant `tenant_admin` - Expect: `policy_gate_status = allow` · `flag_eval_status = evaluated` - Flag panel may return `source=approval_missing` because the registry flag still carries `audit.last_approval_ref=null` — this is **honest**: the policy engine approved the *actor's* access, but the flag itself has its own approval gate inside the evaluator. The demo surfaces both truths instead of hiding the second one. ## Preset: self-view · end_user own Illustrates the precedence rule prec-3 (self-view exception): - `actor_user_id == target_user_id` · `flag_key = hints.registry_v1` (non-sensitive) - Policy precheck skipped (non-sensitive) · flag eval proceeds. - If you flip `flag_key` to a sensitive one the policy panel still allows because self-view is unmasked. ## Why this page is not called "Dashboard" Per `docs/planning/ia-governance.json` `hard_rules[5]`: the word **Dashboard** is reserved for real dashboard experiences (charts · drilldown · interactive). This is a composer + two result panels + a combined trace — not a dashboard. The IA-correct name is **Integration Demo**. ## Why this page does not live under `/kb/*` Per `hard_rules[1]`: `/kb/*` is B-owned. This surface chains two A-owned services; it is A-owned and belongs under `/runtime/*`. ## Why this page is not a top-level main-console button Main console stays at 6 buttons per IA soft caps. Discoverability for this demo flows through: - Index Portal search (title + full-text) - Back-links from `/runtime/feature-flags-service/service.html`, `/runtime/admin-control-plane-service/service.html`, and `/planning/cross-runtime-integration.html` ## Files in this folder ``` docs/runtime/cross-runtime-integration/ ├── index.html ← demo surface (this doc refers to) ├── demo_contract.json ← orchestration contract · request/response shapes · error modes · deferred └── README.md ← this file ```