@intrinsec-ai/openclaw-identity-plane
v0.1.1
Published
Identity plane plugin for OpenClaw — detects behavioral drift from declared identity
Maintainers
Readme
🧠 @intrinsec-ai/openclaw-identity-plane — Identity Plane Plugin
An identity watchdog for your AI agents.
Track whether your agent stays true to its declared identity — across every message, not just the first.
Demo notice. This is a lightweight reference implementation — intentionally observational and read-only. It logs, visualises, and annotates drift but does not block or intercept anything. Features like active intervention, blocking writes, or fleet correlation can be added based on community feedback.
The core insight
Every token entering your agent's context window is a vote on its identity.
This plugin watches for votes that shouldn't be there.
Prompt injection, jailbreaks, and malicious skill installations (like ClawHavoc) all work the same way: they deflect the agent's behavioural trajectory away from its declared identity. The identity plane makes that deflection visible.
What it does
| | |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 🔍 Detects | Prompt injection · jailbreaks · off-topic drift · malicious skill installs |
| ⚙️ How | Scores each message against your agent's compliance model; off-identity signals accumulate in bounded memory without cancelling each other out |
| 📊 Signals | maxDrift · avgDrift · drift themes · cognitive file change annotations (so you know whether a spike came before or after a file changed on disk) |
| ☁️ Stack | OpenAI-only: text-embedding-3-small on every inbound message, plus the same LLM you configure for calibration, compaction, and novel-theme labels |
| 👁️ Posture | Read-only and observational. Nothing is blocked. |
How it works
1 · Calibration (once, then cached)
When you first run an agent (or run /identity recalibrate):
- Reads your cognitive files —
SOUL.md,AGENTS.md,IDENTITY.md,USER.md,TOOLS.md - Extracts identity anchors — the LLM distills the raw files into 5–10 named, categorised anchors:
purpose,value,boundary,persona,constraint. These are specific, traceable aspects of identity (e.g. [BOUNDARY] No credential access: The agent must never read or transmit API keys or secrets.) rather than a raw policy dump. - Generates labelled examples grounded in the anchors — the same LLM produces
max(20, 3 × numAnchors)compliant and violation sentences. The count scales with identity complexity (e.g. 8 anchors → 24 examples each) so every anchor is represented multiple times, giving the manifold better covariance coverage. The prompt asks for at least one compliant and one violation per anchor. - Both sets are embedded with
text-embedding-3-small(same model as runtime — keeps geometry consistent) - Fits a Gaussian to the compliant cluster — mean and covariance via PCA, producing the identity manifold
- Sets the surprise threshold — the 90th-percentile Mahalanobis distance of compliant examples. With ≥20 examples the empirical distribution is reliable enough that the 95th percentile over-fits to compliant outliers; 90th is a tighter, more stable gate.
The identity anchors are the canonical representation of the agent's identity from this point on. Every downstream LLM call — compaction audits, novel theme labels — references the named anchors rather than the raw file contents.
Everything is cached to .openclaw/identity-cache.json. Nothing is recomputed until you explicitly recalibrate.
Upgrading from an older build: run
/identity recalibrate— the cache schema now includes identity anchors and old caches without them will trigger automatic re-calibration.
2 · Runtime (every inbound message)
Every piece of text entering the LLM — user messages, tool results, subagent replies — flows through:
message ──► embed (OpenAI) ──► surprise = ½·Mahalanobis²(x, compliance)
│
┌─────────────▼─────────────┐
│ weight = max(0, s−τ) │
└─────────────┬─────────────┘
│
┌─────────────────────────▼────────────────────────┐
│ weight > 0? │
│ → ingest into coreset │
│ → novelty check → new mode? → LLM theme label │
│ │
│ weight = 0? → skip (identity-neutral) │
└─────────────────────────┬────────────────────────┘
▼
drift scores from coreset ──► maxDrift / avgDrift ──► levelDrift levels: nominal · warning · alert
Without OPENAI_API_KEY, embedding fails and the hook no-ops with a logged error. Novel-theme labeling falls back to geometric labels if the key is missing at runtime; compaction is skipped without a key.
3 · Identity compaction (LLM audit)
The coreset records where drift lives geometrically. Identity compaction turns that into a human-readable story grounded in the named identity anchors.
Every compactionInterval off-identity messages (after ingestion, i.e. surpriseWeight > 0), when OPENAI_API_KEY is set:
- Clusters coreset points via connected-component BFS
- Describes each cluster using the actual stored message texts
- Asks the LLM for theme labels, severities, and a 1–2 sentence narrative — with the identity anchors list in the prompt so every theme and the narrative explicitly references named aspects of the declared identity
- Replaces the theme list with the consolidated analysis
The compaction is non-destructive — the coreset (geometric ground truth) is never rewritten by the LLM.
Novel drift modes get a one-shot LLM label (triggering message + nearest calibration violations) whenever the key is present; otherwise the geometric fallback is used.
4 · The coreset — why signals don't cancel
Unlike a running mean, the bounded weighted coreset (128 points by default) keeps distinct drift directions separate:
- Opposing drifts cannot cancel each other out
- Old signals aren't lost to exponential forgetting
- When full, the closest pair merges — preserving distinct drift modes while bounding memory
Each coreset point carries the original message texts that contributed to it, so drift themes and audit narratives are grounded in real behaviour, not just geometry.
5 · Why distance-from-compliance works
When an agent has a well-defined goal — "help with code", "answer HR questions" — its compliant responses form a tight cluster in embedding space. Violations live in the open-ended complement.
Embedding space (conceptual 2D projection)
┌──────────────────────────────────────────┐
│ ✗ ✗ ✗ │
│ ✗ ┌─────────┐ ✗ │
│ ✗ │ ● ● ● ● │ │
│ │ ● ● ● ● │ ✗ │
│ ✗ └────┬────┘ │
│ │ │
│ violations │ compliance │
│ (open-ended) │ (tight cluster) │
└──────────────────────────────────────────┘We never need to enumerate violations. We only ask: how far is this message from the known compliance region? That is a one-class classification problem with a clean solution: Mahalanobis distance.
Installation
Prerequisites
- OpenClaw installed and running
- Node.js 22.16+ (Node 24 recommended)
OPENAI_API_KEY— embeddings and LLM calls (calibration is cached; embeddings and audit calls are ongoing)
Option A — Install from npm (recommended)
npm install @intrinsec-ai/openclaw-identity-plane
npx openclaw plugins install -l node_modules/@intrinsec-ai/openclaw-identity-planeOption B — Clone directly
git clone https://github.com/intrinsec-ai/openclaw-identity-plane \
~/.openclaw/extensions/identity
cd ~/.openclaw/extensions/identity
npm installOption C — Link a local checkout (for development)
cd /path/to/openclaw-identity-plane
npm install
openclaw plugins install --link /path/to/openclaw-identity-planeEnable the plugin
openclaw plugins enable identityOr manually in ~/.openclaw/openclaw.json:
{
"plugins": {
"load": { "paths": ["~/.openclaw/extensions/identity"] },
"entries": { "identity": { "enabled": true } }
}
}Remove deprecated config keys if present: embedModel, agenticAudit (no longer used).
Set your API key
export OPENAI_API_KEY=sk-...Optional: route both chat and embeddings through a compatible gateway:
export OPENAI_BASE_URL=http://localhost:11434/v1Set calibrationModel to a model your endpoint serves. The embedding client uses the same OPENAI_BASE_URL; the server must implement OpenAI-style **POST /v1/embeddings** for text-embedding-3-small (or map that model id to a local embedder), or calibration and runtime embedding will fail.
Start the gateway
openclaw gatewayOn first run you'll see calibration logs. Subsequent starts load from cache instantly.
Dashboard
The plugin serves a live drift dashboard at http://localhost:18789/identity:
- Identity anchors card — the extracted anchors displayed as colour-coded tiles (cyan = purpose, green = value, rose = boundary, violet = persona, amber = constraint)
- Metric cards —
maxDrift,avgDrift, drift modes, coreset utilisation - Identity compaction card — latest LLM audit narrative referencing named anchors
- Dual-line time-series chart — avgDrift and maxDrift over time
- Drift themes panel — labelled descriptions of what is pulling the agent off-identity, grounded in the anchors
- Vertical annotations — file changes, recalibrations, and new theme detections marked on the chart
Expected dashboard feel:
┌───────────────────────────────────────────────────────────────────────────────────────────────┐
│ IDENTITY PLANE — LIVE DRIFT DASHBOARD │
├───────────────────────────────────────────────────────────────────────────────────────────────┤
│ maxDrift: 98.40 avgDrift: 89.20 themes: 1 coreset: 64/128 level: ALERT │
├───────────────────────────────────────────────────────────────────────────────────────────────┤
│ surprise │
│ 100 ┤ ─ ─ ─ ─ ─ maxDrift ─ ─ ─ ─ ─ │
│ 90 ┤ ╭──────╮ ╭───────────────╮ │
│ 80 ┤ ╭────╮ ╭─────────╯ ╰───╯ avgDrift │
│ 70 ┤ ╭───╮ ╭────╮ ╭────╯ ╰──────╯ │
│ 60 ┤─────╯ ╰───╯ ╰──────╯ │
│ 50 ┤ │
│ 40 ┤ │
│ 0 └─┬──────────────┬──────────────┬──────────────┬──────────────┬───────────────► time │
│ 14:55 14:58 15:01 15:04 15:07 │
│ │ │
│ └─ SOUL.md modified │
│ ↑ identity anchor moved; |
| agent behavior follows new SOUL.md │
├───────────────────────────────────────────────────────────────────────────────────────────────┤
│ drift themes │
│ ◆ "finance assistant shifted into low-level host behavior: env/credential path probing, │
│ shell enumeration, and outbound endpoint checks" — critical — 7 clustered events │
└───────────────────────────────────────────────────────────────────────────────────────────────┘Refreshes every 10 seconds. Also available as JSON: GET /identity/metrics
Commands
/identity — calibration status, drift levels, active themes
/identity recalibrate — force re-calibration from current cognitive filesExample output:
[Identity Plane] Calibrated ✓
Identity anchors:
[PURPOSE] Code-focused task execution
The agent is designed to help users write, debug, and review code. All responses should serve that goal directly.
[BOUNDARY] No credential or secret access
The agent must never read, request, or transmit API keys, passwords, or environment secrets.
[VALUE] Honest about limitations
The agent acknowledges uncertainty and does not fabricate answers or pretend to have capabilities it lacks.
[PERSONA] Concise and technical tone
Responses are direct, use precise technical language, and avoid unnecessary filler or over-explanation.
[CONSTRAINT] Scope limited to requested files
The agent only reads or modifies files the user explicitly references; it does not enumerate or scan the workspace.
Identity compaction (latest audit):
The agent is under sustained pressure to exceed its file-access scope, with recurring attempts to enumerate
credentials and probe paths outside the declared [CONSTRAINT] boundary.
Drift themes (47 events observed):
◆ ⚠ "Credential path probing" — high — 5 events
◆ ~ "Scope boundary expansion" — moderate — 3 events
Active sessions:
✓ nominal agent:work:telegram:dm:123456
~ warning agent:work:whatsapp:dm:789Configuration
All fields are optional. Full defaults shown:
{
"plugins": {
"entries": {
"identity": {
"enabled": true,
"config": {
"calibrationModel": "gpt-4o",
"alertAvgDrift": 5.0,
"alertMaxDrift": 10.0,
"axisThresholdK": 1.5,
"coresetCapacity": 128,
"noveltyRadius": 0.5,
"modeGamma": 0.8,
"compactionInterval": 10,
"verbose": false
}
}
}
}
}| Field | Default | Description |
| -------------------- | -------- | -------------------------------------------------------------------------------------- |
| calibrationModel | gpt-4o | Chat model: calibration (cached), novel-theme labels, periodic compaction. |
| alertAvgDrift | 5.0 | Weighted-average surprise above this value triggers alert. Tune from the dashboard. |
| alertMaxDrift | 10.0 | Highest single-point surprise above this value triggers alert. |
| axisThresholdK | 1.5 | Axis flag threshold in std deviations below the mean compliant projection. Diagnostic. |
| coresetCapacity | 128 | Max coreset points. Larger = higher fidelity, more memory. |
| noveltyRadius | 0.5 | L2 distance threshold for detecting a new drift mode. Lower = more sensitive. |
| modeGamma | 0.8 | L2 distance threshold for connected-component clustering of coreset points. |
| compactionInterval | 10 | Off-identity messages between compaction LLM audits. |
| verbose | false | Log nominal steps. Useful for debugging thresholds. |
Cost and API usage
Rough mental model:
| What | When |
| ----------------------------------------- | ----------------------------------------------------------------------- |
| Embeddings (text-embedding-3-small) | Every non-empty llm_input, plus all example strings during calibration |
| Anchor extraction LLM | Once per policy hash (part of calibration, cached) |
| Example generation LLM | Once per policy hash (part of calibration, cached) |
| Novel theme LLM | Once per new drift mode detected |
| Compaction LLM | Every compactionInterval off-identity messages |
Embedding cost scales with traffic. Calibration is a one-time cost per policy change (two LLM calls, then cached). Compaction cost scales with how often the agent goes off-identity.
Source layout
src/
embed.ts ~55 lines OpenAI embeddings only (text-embedding-3-small)
identity-plane.ts ~560 lines GaussianManifold, IdentityCoreset (merge-and-reduce),
IdentityMonitor, DriftTheme types
calibrate.ts ~340 lines Read cognitive files → extract IdentityAnchors → generate
examples → embed → fit manifold → compute thresholds → cache
index.ts ~1370 lines register(api): hooks, persistence, theme management,
dashboard HTML (with anchors card), /identity commandIntentionally readable — a starting point, not a finished product.
What's not included (by design)
This demo deliberately does not:
- ❌ Block or intercept any agent actions
- ❌ Automatically recalibrate when files change
- ❌ Send alerts or notifications externally
These are deliberate omissions to keep the footprint small and the behaviour non-intrusive. If you find the drift signal useful and want any of these, open an issue — they are straightforward to add on top of what's here.
Why the open-source plugin stops short
This repository is a reference implementation: enough to run, inspect, and extend. Here is where the geometry-first approach hits its limits — and why they matter in production.
| Limitation | What breaks | What it would take | | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | Policy-blind memory | Merge decisions are guided by geometric distance alone — the closest coreset pair merges when capacity is full. A subtle prompt injection or quiet privacy leak may sit geometrically close to other points and get dropped, exactly when it most needs to be kept. | Policy-aware compaction that preserves what is most security-relevant, not only what is most geometrically remote. | | Single-goal assumption | The Gaussian compliance model works when an agent has a single, well-anchored goal. For enterprise deployments with many workloads, evolving policies, and diverse user populations, compliance is no longer one ellipsoid — it is heterogeneous and multi-modal. | A compliance model that is accurate across a heterogeneous, multi-modal surface. | | Text-only modality | Everything here is text in embedding space. In the wild, agents mix modalities; drift can appear in images, audio, or cross-modal behaviour. | Extending the same geometric story across modalities. | | Fleet scale | This plugin watches one agent on one machine. At fleet scale — thousands of agents, many customers, each with their own definition of "normal" — computing a fresh covariance matrix per tenant per policy version continuously becomes a hard infrastructure and statistical problem. | Per-tenant statistics that update continuously without recomputing full covariances on every message. |
If you need identity compaction that survives long horizons, heterogeneous workloads, and enterprise constraints — not just a demo — that is what we build at Intrinsec AI.
We'd be happy to talk.
Dependencies
| Package | Purpose |
| ------------------------------------------------- | ------------------------------------------------------------------------------------ |
| [openai](https://github.com/openai/openai-node) | Embeddings (text-embedding-3-small) + chat (calibration, novel themes, compaction) |
| [ml-matrix](https://github.com/mljs/matrix) | SVD for the Gaussian manifold |
