117 lines
4.1 KiB
Markdown
117 lines
4.1 KiB
Markdown
# Scene 12 — Hot Reload: Change Policy, See It Live
|
|
|
|
**Mechanism:** File watcher polls `policies.yaml` every 2 seconds — no restart needed
|
|
**Expected result:** Edit the YAML, next request uses the new rules
|
|
|
|
---
|
|
|
|
## Presentation Rule
|
|
|
|
> **Language:** All AI narration and communication during scenes must be in **English**.
|
|
>
|
|
> The AI must narrate every interaction with the gateway in detail. **Before** calling the gateway, explain what it is about to do, which endpoint it will call, what payload it will send, and what it expects to happen. **After** receiving the response, explain what the gateway returned, what the decision means, and why it matters. The goal is to make the audience understand exactly what is happening between the AI and the gateway at every step.
|
|
|
|
---
|
|
|
|
## Communication Flow
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant AI as AI Agent
|
|
participant GW as Gateway
|
|
participant PE as Policy Engine
|
|
participant JR as Jira Service
|
|
participant FS as policies.yaml
|
|
|
|
Note over AI,FS: Step 1 — FINANCE is not in allowlist
|
|
AI->>GW: POST /api/gateway/execute<br/>{tool: jira.createTicket,<br/>arguments: {project: FINANCE}}
|
|
GW->>PE: Evaluate policy rules
|
|
PE->>PE: Argument allowlist: FINANCE ✗
|
|
PE-->>GW: Decision: DENY
|
|
GW-->>AI: {decision: DENY, reason: "Project not in allowlist"}
|
|
|
|
Note over FS: Step 2 — Edit YAML: add FINANCE to allowlist
|
|
FS->>GW: @Scheduled poll detects file change (2s)
|
|
GW->>GW: Reload PolicyConfig (AtomicReference swap)
|
|
|
|
Note over AI,FS: Step 3 — FINANCE is now allowed
|
|
AI->>GW: POST /api/gateway/execute<br/>{tool: jira.createTicket,<br/>arguments: {project: FINANCE}}
|
|
GW->>PE: Evaluate policy rules (new config)
|
|
PE->>PE: Argument allowlist: FINANCE ✓
|
|
PE-->>GW: Decision: ALLOW
|
|
GW->>JR: Create Jira ticket
|
|
JR-->>GW: Ticket created
|
|
GW-->>AI: {decision: ALLOW, result: {key: "FINANCE-123"}}
|
|
```
|
|
|
|
## Demo Script
|
|
|
|
> **Presenter says:** "Policy is data, not code. Let me prove it. I'm going to change a rule in the YAML file and the gateway picks it up automatically — no restart, no redeploy."
|
|
|
|
### Step 1 — Baseline: FINANCE project is denied
|
|
|
|
Example request:
|
|
|
|
> "Create a Jira ticket in project FINANCE with summary 'Q1 budget review'"
|
|
|
|
**Result:** DENY — "Project not in allowlist" (only SAFE, PLATFORM, OPS are allowed).
|
|
|
|
### Step 2 — Edit policies.yaml live
|
|
|
|
Open `files/config/policies.yaml` and add FINANCE to the allowlist:
|
|
|
|
```yaml
|
|
data:
|
|
allowed_projects: [SAFE, PLATFORM, OPS, FINANCE] # <-- added FINANCE
|
|
```
|
|
|
|
> **Presenter says:** "I just added FINANCE to the allowlist. No rebuild, no restart. Let's wait 2 seconds and try again."
|
|
|
|
### Step 3 — Retry: FINANCE is now allowed
|
|
|
|
Repeat the request:
|
|
|
|
> "Create a Jira ticket in project FINANCE with summary 'Q1 budget review'"
|
|
|
|
**Result:** ALLOW — the gateway reloaded the policy file and FINANCE is now in the allowlist.
|
|
|
|
### Step 4 — Revert (optional)
|
|
|
|
Remove FINANCE from the allowlist to restore the original policy:
|
|
|
|
```yaml
|
|
data:
|
|
allowed_projects: [SAFE, PLATFORM, OPS]
|
|
```
|
|
|
|
---
|
|
|
|
## What This Proves
|
|
|
|
- **Policy is data** — changing behavior = editing a YAML file, not recompiling Java
|
|
- The gateway polls the file every 2s (`gateway.policy.reload-interval-ms`), compares last-modified time, and reloads atomically if changed
|
|
- Zero downtime, zero restart — the `AtomicReference<PolicyConfig>` swap is lock-free
|
|
- If the new file is malformed, the gateway keeps the last valid config (fail-safe)
|
|
- This is the stepping stone to OPA: today you edit YAML, tomorrow OPA pulls policy bundles from a registry
|
|
|
|
---
|
|
|
|
## How It Works
|
|
|
|
```
|
|
policies.yaml modified on disk
|
|
→ @Scheduled poll (every 2s) detects lastModified change
|
|
→ YAML parsed → PolicyConfig object
|
|
→ AtomicReference.set(newConfig)
|
|
→ Next request calls Supplier<PolicyConfig>.get() → gets new config
|
|
→ Policy evaluated with new rules
|
|
```
|
|
|
|
No restart. No endpoint. Just the file.
|
|
|
|
---
|
|
|
|
## Key Message
|
|
|
|
> "A natural next step is OPA bundle sync or another remote policy source. The principle stays the same: policy changes should not require code changes or redeployments."
|