Back to Blog

Authorizing Claude Code with Stratium

A reference architecture for putting every Claude Code tool call under Zero Trust enforcement — without changing a line of code in any project.

June 18, 2026

TL;DR

  • Claude Code ships a hook system (PreToolUse, PostToolUse, UserPromptSubmit) that lets external code intercept every tool call before it executes. Stratium ships @stratium/claude-hooks, a Node CLI that wires those hooks to a Stratium Agent Gateway.
  • The result: every Claude Code tool call on every developer machine in the fleet is identified, delegated, authorized, and audited — with zero changes to any project codebase. Policy is set per project in CLAUDE.md and centrally via the Stratium PAP.
  • Default fail-closed: if the Stratium gateway is unreachable, tools don’t execute. Default read-only: new agents are capped at tier-1 (READ_ONLY) until an admin grants higher. Default auditable: every decision lands in ~/.claude/stratium-audit.jsonl and on the platform’s audit stream.

Why Claude Code Is the Cleanest Surface for Agent Authorization Today

Most agent runtimes ask you to wrap tool calls in custom code. Claude Code doesn’t. It exposes three hook points that fire around every tool invocation, and the hooks can return a non-zero exit code to block the call. That’s a control point that doesn’t require modifying a single project repo.

UserPromptSubmit

Fires when a developer sends the first prompt of a session. Stratium uses this to authenticate the user against Keycloak, mint a delegation, and load the per-project policy from CLAUDE.md.

PreToolUse

Fires before every single tool call (Read, Write, Edit, Bash, Grep, Glob, etc.). Stratium calls ExecuteAction against the Agent Gateway. If the decision is DENY, the hook exits non-zero and Claude Code surfaces the reason inline to the developer.

PostToolUse

Fires after every successful tool call. Stratium uses this for audit logging only — never blocks. The audit record includes outcome, resource, agent identity, delegation ID.

That’s the entire integration surface. Three scripts in ~/.claude/settings.json. Every Claude Code session on every machine in your fleet now flows through Stratium.

The Reference Architecture

┌─────────────────────────────────────────────────────┐ │ Developer Machine │ │ │ │ ┌─────────────┐ │ │ │ Claude Code │ ─── fires hook ──┐ │ │ └─────────────┘ ▼ │ │ ┌───────────────────────┐ │ │ │ PreToolUse hook │ │ │ │ (@stratium/ │ │ │ │ claude-hooks) │ │ │ └─────────┬─────────────┘ │ └────────────────────────────────────┼─────────────────┘ │ grpcurl ExecuteAction │ ▼ ┌─────────────────────────────────────────────────────┐ │ Stratium (corp-deployed) │ │ │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ Agent Gateway │ ──▶│ Platform Service │ │ │ │ :50054 │ │ :50051 │ │ │ │ ExecuteAction │ │ GetDecision │ │ │ └──────────────────┘ └─────────┬────────┘ │ │ │ │ │ │ │ ▼ │ │ │ ┌─────────────────────┐ │ │ │ │ PAP (policy store) │ │ │ │ │ + Audit log │ │ │ │ └─────────────────────┘ │ │ ▼ │ │ ┌──────────────────┐ │ │ │ Keycloak │ │ │ │ (OIDC) │ │ │ └──────────────────┘ │ └─────────────────────────────────────────────────────┘

On the developer machine: three scripts wired into Claude Code’s hook system, plus a per-session state file at /tmp/stratium-session-<pid>.json that caches the delegation token. On the corporate side: a standard Stratium deployment with Agent Gateway, Platform Service, PAP, Keycloak, and an audit log.

The Whole Setup, on One Machine

For an individual developer or an evaluation environment, the install is three commands:

# 1. Install the hook package $ npm install -g @stratium/claude-hooks # 2. Point it at your corporate Stratium gateway $ stratium-hooks configure --gateway stratium.corp.com:50054 # 3. Authenticate against Keycloak $ stratium-hooks login Username: alice@corp.com Password: ******** [OK] logged in as alice@corp.com

Configure writes ~/.claude/stratium.json (machine-level defaults), registers the hook scripts in ~/.claude/settings.json, and is done. Open Claude Code as normal — the next tool call is the first one under Stratium authorization.

Fleet Rollout via MDM

For an engineering org, individual installs don’t scale. Push the configuration via your existing endpoint management. The package supports Jamf, Intune, Workspace ONE, and any other MDM with a custom-script capability.

# Jamf Policy / Intune Remediation Script STRATIUM_GATEWAY=stratium.corp.com:50054 \ STRATIUM_PAP=https://stratium.corp.com:8090 \ STRATIUM_KEYCLOAK=https://keycloak.corp.com \ ./node_modules/.bin/stratium-hooks configure --silent

Pair that with a SSO-backed Keycloak realm and developers never see a separate login — the existing corporate identity is the agent’s delegator. The first prompt of each session prompts the hook to mint a fresh delegation, scoped to whatever the current project’s CLAUDE.md permits.

Per-Project Policy: Just a Block in CLAUDE.md

Projects declare what Claude Code is allowed to do in their own CLAUDE.md. Stratium reads the block on session start and uses it to scope the delegation. No external config file. No IDE plugin. The repo carries its own policy.

## Stratium Authorization stratium: enabled: true agent_name: claude-code max_action_tier: READ_ONLY approved_tools: - read_file - list_files - grep_search - web_search on_unavailable: fail_closed purpose: "Development work on my-project"

For projects that legitimately need more — CI maintenance, infrastructure work, scripted migrations — an admin can grant higher tiers per project, per tool. The tool_tiers map lets you cap most tools at READ_ONLY while permitting one specific tool to INTERNAL_MODIFY:

stratium: max_action_tier: READ_ONLY tool_tiers: Bash: INTERNAL_MODIFY # bash up to tier 2, even though default cap is 1

The decision happens at the gateway. The developer never has to think about it. The policy lives in the repo and travels with it.

What Each max_action_tier Actually Permits

The hook ships a default mapping from Claude Code’s built-in tools to Stratium’s five action tiers. The mapping is overridable per project but well-chosen by default.

REASONING (0)

No tool calls at all. Pure conversation with the model. Useful when a developer wants planning help without any side effects.

READ_ONLY (1)

Read, Grep, Glob, WebFetch, WebSearch. No side effects. The fleet default.

INTERNAL_MODIFY (2)

Write, Edit, Bash (non-network). Local file edits, local builds, local test runs. Tier-2 trust required.

EXTERNAL_COMMS (3)

Bash with curl / wget / ssh / git push / cloud CLIs. Anything that crosses the machine boundary outbound.

DESTRUCTIVE (4)

rm -rf, drop tables, force push to main, infrastructure deletes. Default-deny for everything below tier-3 agent trust. The places you can’t roll back from.

What a Developer Sees When a Call Is Blocked

The hook’s most important UX choice: when a tool is denied, Claude Code surfaces the reason inline as if it were any other tool error. The model continues reasoning with the denial as new context — usually deciding to ask the developer what to do.

I attempted to write to src/auth.go but was blocked by Stratium: Tool: Write Decision: DENY Reason: INTERNAL_MODIFY (tier 2) exceeds your delegation cap for this project (READ_ONLY, tier 1) Your delegation for this session allows: read_file, list_files, grep_search To write files in this project, ask your admin to update the stratium: block in CLAUDE.md to include Write and set max_action_tier: INTERNAL_MODIFY. Delegation ID: cc0c4885 (for your admin’s reference)

The delegation ID gives the admin everything they need to inspect the chain in the PAP UI — what user, what agent, what scope, what hierarchy caps. The developer gets the change-request path. The admin gets the audit trail. No one gets blocked silently.

The Audit Stream

Every tool call — whether it succeeded or was blocked — generates a structured audit event. Two places to read it:

Local: ~/.claude/stratium-audit.jsonl

Every developer machine writes one JSONL line per tool invocation. Useful for incident response when the developer’s laptop has the only memory of what happened.

Central: Stratium Platform Audit Log

Every GetDecision call lands in the platform’s audit log with subject + agent + delegation + action + resource + decision + reason + evaluated policy + timestamp. Ship it to your SIEM via your existing log pipeline.

A representative line from the local log:

{ "ts": "2026-06-18T10:15:32Z", "event": "tool_executed", "tool_name": "Read", "action": "read", "action_tier": 1, "tier_label": "READ_ONLY", "outcome": "success", "user": "alice@corp.com", "agent_id": "a3f7c291", "delegation_id": "cc0c4885", "resource": { "type": "file", "path": "src/auth.go" } }

The Fail-Closed Default Is Load-Bearing

If the Stratium gateway is unreachable — corporate VPN dropped, network partition, gateway down — the hook fails closed by default and no tools execute. The developer sees an explicit error explaining why, not silent allow-through. That’s a configuration decision that matters; the alternative is exactly the “authentication passed, authorization didn’t” failure mode this whole platform exists to prevent.

stratium-hooks configure --gateway stratium.corp.com:50054 --fail-closed # or override per-project in CLAUDE.md stratium: on_unavailable: fail_closed # default

fail_open is available for non-production environments. Don’t use it in production. The whole point is that the call doesn’t happen unless authorization succeeded.

Common Pitfalls When You Roll This Out

Setting fail_open “Just for the Pilot”

You will forget to flip it back. The default exists for a reason. Run the pilot with fail_closed from day one — the failure modes you discover are the ones you actually need to fix.

No stratium: Block in CLAUDE.md

Without a project block, the machine-level default applies (usually READ_ONLY, fail-closed). Developers will report “Claude is blocked from everything” on day one. Fix: ship a template CLAUDE.md with a sensible default block for your repos.

Sharing One Agent ID Across the Fleet

The whole compound-decision story requires each developer’s session to register against the same agent type but with the developer’s own delegation. Don’t share delegation tokens. Mint one per session.

Forgetting About PostToolUse

The audit hook is non-blocking, but it’s where the truth of what happened lives. If you skip it during the pilot, you have no incident-response surface when something does go wrong. Ship all three hooks together.

The 30-Day Rollout

Day 1–7

One Developer, One Repo, Fail-Closed

Install @stratium/claude-hooks on one machine. Configure against a Stratium instance (Docker Compose is fine for the pilot). Add a stratium: block to one repo’s CLAUDE.md. Run a normal day’s work. Watch the audit log fill in.

Day 8–14

Pilot Team Rollout

5–10 developers. Push install + configure via MDM. Standardize the CLAUDE.md block in 3–5 representative repos at different sensitivity levels. Track DENY counts — they tell you which projects need more capacity.

Day 15–21

Policy Refinement and SIEM Wiring

Tune the policy library based on real pilot DENY traffic. Wire Stratium’s central audit log into your SIEM. Add Codex hooks (@stratium/codex-hooks) if your fleet uses both.

Day 22–30

Fleet-Wide Default

Push to the rest of engineering via MDM. Make the stratium: block in CLAUDE.md a required template field for new repos. Every Claude Code session in the org now flows through Stratium.

See It Running

A 30-minute walkthrough: a real Claude Code session under @stratium/claude-hooks, a real DENY surfaced inline, the matching audit event in the platform stream, and a tour of the PAP UI where policy is administered. We bring the stack; you bring an idea of which agents on your fleet you’d most like to put under authorization first.

Book a Claude Code rollout walkthrough Explore the Platform

Read Next

Three hook scripts. Every Claude Code tool call in your org, authorized.