# Bedrock Quota Monitor + Data Isolation

See your Bedrock spending at a glance — right in the Claude Code status line. And stay protected: this plugin automatically prevents Bedrock conversation history (which may contain client data) from leaking to the Anthropic API via session resume.

```
Claude Opus 4.6 │ API $7.50 │ 🔴 3h:85% day:67% wk:42% │ ████████░░░░ 42% 85k/200k
```

## What it does

- Shows your **3-hour, daily, and weekly** Bedrock spend as a percentage of your quota
- Warns Claude about high quota usage so it tells you proactively
- Uses color-coded emoji: 🟢 ok → 🟡 caution → 🔴 warning → 🚫 blocked
- **Blocks resuming a Bedrock session on the Anthropic API** — prevents client data from leaking via JSONL transcript replay
- Everything is **automatic** — no configuration needed after install

## Setup (2 minutes)

### 1. Install the plugin

From the Gemma toolkit marketplace in Claude Code:

```
/plugins
```

Then enable `gemma-bedrock-monitor`.

Or install directly:

```bash
claude plugins add gemma-bedrock-monitor@gemma-toolkit
```

### 2. Start a Bedrock session

```bash
bedrock-on
claude
```

That's it! The plugin will:
- Automatically configure your status line (backs up your current one)
- Query your quota usage from CloudWatch
- Show spending indicators after every response

### 3. Verify it works

In your Bedrock Claude session, check the status line at the bottom. If your spending is low (below 40% of any quota window), the status line will look normal — that means it's working, there's just nothing to warn about yet.

To test with simulated high usage:

```bash
# Create a fake cache with 85% usage
echo '{"cost_3h":8.5,"cost_day":12.0,"cost_week":48.0,"limit_3h":10.0,"limit_day":15.0,"limit_week":60.0,"user":"test","updated_at":"2026-01-01T00:00:00Z"}' > /tmp/bedrock-quota-cache-$(whoami).json

# Start claude — you should see 🔴 on the status line
claude

# Clean up after testing
rm /tmp/bedrock-quota-cache-$(whoami).json
```

## How it looks

| Your usage | Status line | Claude warns you? |
|---|---|---|
| Below 40% | Clean (no quota shown) | No |
| 40-49% | 🟢 `3h:45% day:30%` | No |
| 50-79% | 🟡 `3h:65% day:50% wk:30%` | Yes, at session start |
| 80-99% | 🔴 `3h:85% day:67% wk:42%` | Yes, with urgency |
| 100%+ | 🚫 `3h:105% day:107% wk:108%` | Yes — you're blocked |

## How it works under the hood

```
Quota Enforcer Lambda (every 5 min)
  → publishes spend + limits to CloudWatch (Custom/Bedrock namespace)

Claude Code session starts
  → SessionStart hook queries CloudWatch, caches to /tmp/ (5 min TTL)
  → If spend > 50%, sends systemMessage → Claude warns you
  → Status line reads cache on every response → shows emoji + percentages
```

Data is per-user — you only see your own spend. Quota limits are read automatically from CloudWatch (set by your admin in Terraform).

## Data isolation

### The flaw

Claude Code maintains a full conversation transcript (JSONL) on disk. When you run `claude --resume`, Claude Code replays the entire transcript to the active API endpoint. If you run a database session on Bedrock (where data stays in AWS) and then resume that session on the Anthropic API, the full conversation history — including any client data from database queries — is sent through Anthropic's servers.

Before v1.2.0, the only protection against this was a system-prompt instruction telling Claude not to resume after switching. There was no hard technical enforcement.

### The solution

This plugin adds hard technical enforcement via two hook layers:

**Layer 1 — SessionStart (taint + poison):**
- Every Bedrock session writes a taint marker at `~/.claude/plugins/data/<plugin>/tainted-transcripts/<sha256(transcript_path)>`
- If a tainted transcript is resumed on the Anthropic API, the session is marked poisoned and `additionalContext` is injected into the session instructing Claude to refuse all requests

**Layer 2 — UserPromptSubmit + PreToolUse (hard block):**
- Before every prompt and tool call, the hooks check if the current session is poisoned
- If poisoned, the operation is blocked with `{"decision":"block"}` / `{"permissionDecision":"deny"}`
- This is the enforcement layer — SessionStart can warn but cannot hard-block

The two layers are complementary:
- Layer 1 catches `claude --resume <session-id>` from the command line
- Layer 1 also catches `/resume` inside a running Anthropic session (verified: SessionStart fires with `source=resume` in both cases)
- Layer 2 is the hard gate that ensures no prompt or tool call can slip through even if Layer 1 fails

To continue a Bedrock conversation, always resume it on Bedrock:
```bash
bedrock-on
claude --resume   # or claude --resume <session-id>
```

To start general coding after Bedrock work, open a **fresh** session:
```bash
bedrock-off
claude            # no --resume
```

### Note on /clear

When you run `/clear` inside a Claude Code session, it creates a **new session** with a new session ID and JSONL file. The old transcript is left intact on disk. The new session fires its own `SessionStart` and gets its own taint marker if on Bedrock. Old taint markers are kept permanently (conservative, correct: the old transcript still exists on disk and could theoretically be resumed).

### Verification

This feature was verified end-to-end before release. See [`docs/session-isolation-verification.md`](docs/session-isolation-verification.md) for the full test runbook and results.

## Troubleshooting

**I don't see any quota on the status line**
- Your spending is below 40% of all windows — working as intended!
- Verify you're on Bedrock: `echo $CLAUDE_CODE_USE_BEDROCK` should print `1`
- Check cache exists: `cat /tmp/bedrock-quota-cache-$(whoami).json`

**I get AccessDenied errors**
- Your IAM user needs `cloudwatch:GetMetricData` on `Custom/Bedrock` — ask your admin to apply the Terraform update

**I want my old status line back**
```bash
cp ~/.claude/settings.json.pre-bedrock-monitor.bak ~/.claude/settings.json
```

**Data seems stale**
```bash
rm /tmp/bedrock-quota-cache-$(whoami).json
# Restart claude — cache will refresh
```
