{"_id":"@airlockapp/openclaw-airlock","_rev":"6-f5141d70bf35bf2fb12aa94ae23e1560","name":"@airlockapp/openclaw-airlock","dist-tags":{"latest":"0.4.8"},"versions":{"0.4.2":{"name":"@airlockapp/openclaw-airlock","version":"0.4.2","keywords":["airlock","openclaw","security","human-in-the-loop","approval","enforcer","gateway","harp"],"author":{"name":"Airlock"},"license":"MIT","_id":"@airlockapp/openclaw-airlock@0.4.2","maintainers":[{"name":"aliozgur","email":"aliozgur79@gmail.com"}],"homepage":"https://airlockapp.io","bugs":{"url":"https://github.com/airlockapp/gateway-clients/issues"},"dist":{"shasum":"caf9b078ada5a30bc22da82a2ac07767d0b9fb67","tarball":"https://registry.npmjs.org/@airlockapp/openclaw-airlock/-/openclaw-airlock-0.4.2.tgz","fileCount":47,"integrity":"sha512-wLc3a5sqdKJ2gIqOjXfhjujIixIwyETctj5YrpF2/2/Qvg86muvHyG/Rf3G3Dglqddm5fVUr0nL9+W4y/jYHSA==","signatures":[{"sig":"MEQCID1i4e899fZ4EM7x7/Qj65hb2rEVgq4a9L5UYoYFVV+ZAiBvEjDEawKUIOmSsoiAQOBpBrkCpipQTR1K2YWxCK2l/w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":127999},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"dc459ca4b0ec2259f2b2a06ceaed7ae1173f7de7","scripts":{"dev":"tsc --watch","build":"tsc","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"aliozgur","email":"aliozgur79@gmail.com"},"openclaw":{"entry":"./dist/index.js","compatibleWith":">=0.1.0"},"repository":{"url":"git+https://github.com/airlockapp/gateway-clients.git","type":"git","directory":"src/openclaw-airlock"},"_npmVersion":"10.9.4","description":"Airlock security gateway plugin for OpenClaw — enforces human-in-the-loop approval for AI tool use","directories":{},"_nodeVersion":"22.22.1","dependencies":{"@airlockapp/gateway-sdk":"^0.4.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/openclaw-airlock_0.4.2_1774902179211_0.19475491403913625","host":"s3://npm-registry-packages-npm-production"}},"0.4.3":{"name":"@airlockapp/openclaw-airlock","version":"0.4.3","keywords":["airlock","openclaw","security","human-in-the-loop","approval","enforcer","gateway","harp"],"author":{"name":"Airlock"},"license":"MIT","_id":"@airlockapp/openclaw-airlock@0.4.3","maintainers":[{"name":"aliozgur","email":"aliozgur79@gmail.com"}],"homepage":"https://airlockapp.io","bugs":{"url":"https://github.com/airlockapp/gateway-clients/issues"},"dist":{"shasum":"7968c05f16b301a3d45de9567874133767a4e20d","tarball":"https://registry.npmjs.org/@airlockapp/openclaw-airlock/-/openclaw-airlock-0.4.3.tgz","fileCount":47,"integrity":"sha512-5U3buypSFynE8MFTRXKI5rrE0+cKiNhZzxC1Xdl7fDiaewXkKJps/yv4ZgWNExj4o0xrjpa244Jz3csHn5rm2w==","signatures":[{"sig":"MEYCIQDAJPeW0TwxkkU8PA24wRVyiyXRuk0ScNLJg6ETNM8dpQIhAIZe9lf3sDqUNzsLj4eyNO7OfOKNg+RvfKawvM48yETq","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":128033},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"756de6a3e387b63434fcfe2374ff9e359e6d2046","scripts":{"dev":"tsc --watch","build":"tsc","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"aliozgur","email":"aliozgur79@gmail.com"},"openclaw":{"extensions":{"entry":"./dist/index.js","compatibleWith":">=0.1.0"}},"repository":{"url":"git+https://github.com/airlockapp/gateway-clients.git","type":"git","directory":"src/openclaw-airlock"},"_npmVersion":"10.9.4","description":"Airlock security gateway plugin for OpenClaw — enforces human-in-the-loop approval for AI tool use","directories":{},"_nodeVersion":"22.22.1","dependencies":{"@airlockapp/gateway-sdk":"^0.4.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/openclaw-airlock_0.4.3_1774967280849_0.43172537469748784","host":"s3://npm-registry-packages-npm-production"}},"0.4.4":{"name":"@airlockapp/openclaw-airlock","version":"0.4.4","keywords":["airlock","openclaw","security","human-in-the-loop","approval","enforcer","gateway","harp"],"author":{"name":"Airlock"},"license":"MIT","_id":"@airlockapp/openclaw-airlock@0.4.4","maintainers":[{"name":"aliozgur","email":"aliozgur79@gmail.com"}],"homepage":"https://airlockapp.io","bugs":{"url":"https://github.com/airlockapp/gateway-clients/issues"},"dist":{"shasum":"9955a6d7af5d0398d43588e4703457e0b2ecf4d1","tarball":"https://registry.npmjs.org/@airlockapp/openclaw-airlock/-/openclaw-airlock-0.4.4.tgz","fileCount":47,"integrity":"sha512-2g+A+Az2ftnKdCp6vkjaHBxRCvqTzgi7Qywi2/UGhY1osArrRzNX8NKPmnz2tVxNfpf5IY68lvFztEue5AAkvA==","signatures":[{"sig":"MEQCIHUeEYIeMLhHXbp35UPq0jLNc60WGnkg2QDIlA1NF+bdAiALg73omC9c94L0FdlXxbSd6Y0qcMe+J3GmfU51uI9vsg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":128022},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"dae3a05cf471da4167f52154e976de0d5b2e9d4e","scripts":{"dev":"tsc --watch","build":"tsc","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"aliozgur","email":"aliozgur79@gmail.com"},"openclaw":{"extensions":["./dist/index.js"],"compatibleWith":">=0.1.0"},"repository":{"url":"git+https://github.com/airlockapp/gateway-clients.git","type":"git","directory":"src/openclaw-airlock"},"_npmVersion":"10.9.4","description":"Airlock security gateway plugin for OpenClaw — enforces human-in-the-loop approval for AI tool use","directories":{},"_nodeVersion":"22.22.1","dependencies":{"@airlockapp/gateway-sdk":"^0.4.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/openclaw-airlock_0.4.4_1774967983851_0.34045806568295944","host":"s3://npm-registry-packages-npm-production"}},"0.4.5":{"name":"@airlockapp/openclaw-airlock","version":"0.4.5","keywords":["airlock","openclaw","security","human-in-the-loop","approval","enforcer","gateway","harp"],"author":{"name":"Airlock"},"license":"MIT","_id":"@airlockapp/openclaw-airlock@0.4.5","maintainers":[{"name":"aliozgur","email":"aliozgur79@gmail.com"}],"homepage":"https://airlockapp.io","bugs":{"url":"https://github.com/airlockapp/gateway-clients/issues"},"dist":{"shasum":"e7602139093e1556928d57eef0dd7db68caf085f","tarball":"https://registry.npmjs.org/@airlockapp/openclaw-airlock/-/openclaw-airlock-0.4.5.tgz","fileCount":47,"integrity":"sha512-VBQXxWQAvX5T92e4bU/q0owyBioSVp038cakae03QyOMFRbbVG2m446HvqkWZib5aNVZXtdLbAg6LMesYSEmMA==","signatures":[{"sig":"MEUCIECIkijUMoGeK15fvGStAJgo+yBjtwd5PRkW/yR3UJCqAiEAxw8rpitMybjkBDvgOkwb9XWW+HFvWfyHhXcbo52N7/c=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":130700},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"b7f0af7fbf330f684464816ac823c291593f4061","scripts":{"dev":"tsc --watch","build":"tsc","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"aliozgur","email":"aliozgur79@gmail.com"},"openclaw":{"extensions":["./dist/index.js"],"compatibleWith":">=0.1.0"},"repository":{"url":"git+https://github.com/airlockapp/gateway-clients.git","type":"git","directory":"src/openclaw-airlock"},"_npmVersion":"10.9.4","description":"Airlock security gateway plugin for OpenClaw — enforces human-in-the-loop approval for AI tool use","directories":{},"_nodeVersion":"22.22.1","dependencies":{"@airlockapp/gateway-sdk":"^0.4.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/openclaw-airlock_0.4.5_1775060537080_0.3228819482306813","host":"s3://npm-registry-packages-npm-production"}},"0.4.6":{"name":"@airlockapp/openclaw-airlock","version":"0.4.6","keywords":["airlock","openclaw","security","human-in-the-loop","approval","enforcer","gateway","harp"],"author":{"name":"Airlock"},"license":"MIT","_id":"@airlockapp/openclaw-airlock@0.4.6","maintainers":[{"name":"aliozgur","email":"aliozgur79@gmail.com"}],"homepage":"https://airlockapp.io","bugs":{"url":"https://github.com/airlockapp/gateway-clients/issues"},"dist":{"shasum":"cfec23f4a12e6699473f576d74d394daa19ca62b","tarball":"https://registry.npmjs.org/@airlockapp/openclaw-airlock/-/openclaw-airlock-0.4.6.tgz","fileCount":43,"integrity":"sha512-SRquR5NaiT4gzdiCGMyo52nOzPSci4m94oV6FNzzukw9rn6HObW4/7aenU0SrN4EEUoduG1WxwjPTTHtkVKwEQ==","signatures":[{"sig":"MEUCIQDqDkwx1G295gq0bIM3W4AS4YIBJfenhQoWIOKf9LNwHAIgK5TI20CNkg+zeIkLKC+VmyhQboIFiJjPpbdmes8AkKg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":145902},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"8fa2074cc22fd91a6111ce0227d85a1fa761d463","scripts":{"dev":"tsc --watch","build":"tsc","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"aliozgur","email":"aliozgur79@gmail.com"},"openclaw":{"extensions":["./dist/index.js"],"compatibleWith":">=0.1.0"},"repository":{"url":"git+https://github.com/airlockapp/gateway-clients.git","type":"git","directory":"src/openclaw-airlock"},"_npmVersion":"10.9.7","description":"Airlock security gateway plugin for OpenClaw — enforces human-in-the-loop approval for AI tool use","directories":{},"_nodeVersion":"22.22.2","dependencies":{"@airlockapp/gateway-sdk":"^0.4.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/openclaw-airlock_0.4.6_1775074787352_0.161380343026309","host":"s3://npm-registry-packages-npm-production"}},"0.4.8":{"name":"@airlockapp/openclaw-airlock","version":"0.4.8","description":"Airlock security gateway plugin for OpenClaw — enforces human-in-the-loop approval for AI tool use","main":"dist/index.js","types":"dist/index.d.ts","type":"module","license":"MIT","author":{"name":"Airlock"},"homepage":"https://airlockapp.io","repository":{"type":"git","url":"git+https://github.com/airlockapp/gateway-clients.git","directory":"src/openclaw-airlock"},"bugs":{"url":"https://github.com/airlockapp/gateway-clients/issues"},"keywords":["airlock","openclaw","security","human-in-the-loop","approval","enforcer","gateway","harp"],"openclaw":{"extensions":["./dist/index.js"],"compat":{"pluginApi":">=2026.3.0","minGatewayVersion":"2026.3.0"},"build":{"openclawVersion":"2026.3.12"}},"scripts":{"build":"tsc","dev":"tsc --watch","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"dependencies":{"@airlockapp/gateway-sdk":"^0.4.0"},"devDependencies":{"@types/node":"^22.0.0","typescript":"^5.3.0"},"engines":{"node":">=18.0.0"},"_id":"@airlockapp/openclaw-airlock@0.4.8","gitHead":"e291a1537ef0fbab5896e3e1b198f3c112c5e951","_nodeVersion":"22.22.2","_npmVersion":"10.9.7","dist":{"integrity":"sha512-qApaIk0I3rb/noe9NV/0IG0g1vamlLF7ljaCRguWKvUruF/yOeikZka2fIZ6JJ+66n6ynKD9wLbDKXDEk3EI7Q==","shasum":"7396ef5799c40c101823c9c292f1634dde1d193d","tarball":"https://registry.npmjs.org/@airlockapp/openclaw-airlock/-/openclaw-airlock-0.4.8.tgz","fileCount":43,"unpackedSize":147832,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDl4mh/66W0E01iVPVJSDOfC9GNCZMuf4fk8pu2JRIB0gIhANKEBpWX7CIcVMiajOSY3tmTiHym/7ol6cw7qZ/5xSij"}]},"_npmUser":{"name":"aliozgur","email":"aliozgur79@gmail.com"},"directories":{},"maintainers":[{"name":"aliozgur","email":"aliozgur79@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/openclaw-airlock_0.4.8_1775578571534_0.43509450438208486"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-30T20:22:59.127Z","modified":"2026-04-07T16:16:11.851Z","0.4.2":"2026-03-30T20:22:59.363Z","0.4.3":"2026-03-31T14:28:01.011Z","0.4.4":"2026-03-31T14:39:44.007Z","0.4.5":"2026-04-01T16:22:17.217Z","0.4.6":"2026-04-01T20:19:47.568Z","0.4.8":"2026-04-07T16:16:11.708Z"},"bugs":{"url":"https://github.com/airlockapp/gateway-clients/issues"},"author":{"name":"Airlock"},"license":"MIT","homepage":"https://airlockapp.io","keywords":["airlock","openclaw","security","human-in-the-loop","approval","enforcer","gateway","harp"],"repository":{"type":"git","url":"git+https://github.com/airlockapp/gateway-clients.git","directory":"src/openclaw-airlock"},"description":"Airlock security gateway plugin for OpenClaw — enforces human-in-the-loop approval for AI tool use","maintainers":[{"name":"aliozgur","email":"aliozgur79@gmail.com"}],"readme":"# Airlock Plugin for OpenClaw\n\n[![npm](https://img.shields.io/npm/v/@airlockapp/openclaw-airlock)](https://www.npmjs.com/package/@airlockapp/openclaw-airlock)\n\nEnforces human-in-the-loop approval for risky AI tool actions via the [Airlock Gateway](https://airlockapp.io).\n\n**Airlock Approver** (mobile app): [App Store](https://apps.apple.com/us/app/airlock-approver/id6760250865) · [Google Play](https://play.google.com/store/apps/details?id=com.airlockapp.io)\n\n## Features\n\n- **Tool-based approval** — AI calls `airlock_request_approval` for explicit approval\n- **Hook-based enforcement** — Automatically intercepts protected tool executions\n- **Polling-based decisions** — Long-polls the gateway for approver decisions\n- **Pre-generated pairing** — X25519 ECDH key exchange with mobile approver app\n- **Decision signature verification** — Ed25519 signatures prevent forgery\n- **DND (Do Not Disturb)** — Auto-approves when DND policies are active\n- **Presence heartbeat** — Mobile app shows enforcer online/offline status\n- **State persistence** — Pairing state survives restarts (`.airlock/pairing-state.json`)\n- **Fail modes** — Configurable fail-open or fail-closed on timeout/errors\n\n---\n\n## Installation\n\n### Prerequisites\n\n| Requirement | Details |\n|-------------|---------|\n| **OpenClaw** | An OpenClaw-compatible AI agent runtime |\n| **Node.js** | Version 18 or later (`node --version`) |\n| **Airlock Gateway** | Access to an Airlock Integrations Gateway (default: `igw.airlocks.io`) |\n| **Airlock Mobile App** | For approving/denying actions and generating pairing codes |\n| **Airlock Developer Account** | Required to create an Enforcer App and get API credentials (see below) |\n| **Personal Access Token (PAT)** | Created from the Platform or Mobile App (Settings → Access Tokens) |\n\n### Step 1 — Join the Airlock Developer Programme\n\nBefore you can create an enforcer app, you must register as a developer.\n\n1. **Sign in** to the [Airlock Platform](https://platform.airlocks.io) with your Airlock account.\n2. Navigate to **Developer Programme** in the sidebar.\n3. Fill in the application form.\n4. **Submit** your application. An Airlock administrator will review it.\n5. Once **Approved**, you can create enforcer apps.\n\n> For the full walkthrough with field descriptions and application states, see the [Airlock Developer Guide](https://airlockapp.io/docs/developer-guide/).\n\n### Step 2 — Create an Enforcer App\n\nOnce approved, create an app from **Developer Programme → My Apps** in the Platform web UI.\n\n| Field | Value |\n|-------|-------|\n| **Name** | Your app name (e.g. \"My OpenClaw Enforcer\"). Must be unique. Max 128 characters. |\n| **Kind** | Select **Agent** (confidential client — authenticates via Client ID + Client Secret) |\n| **Is Open Source** | Whether your enforcer source code is public |\n| **Description** | What your app does (max 2000 chars) |\n\nOn creation, you receive:\n\n| Credential | Description |\n|------------|-------------|\n| **App ID** | Human-readable identifier (format: `ABC-1234567`) |\n| **Client ID** | 20-character alphanumeric string (used in plugin config) |\n| **Client Secret** | 40-character secret (**shown only once** — copy and save it!) |\n\n> ⚠️ **Save the Client Secret immediately.** You cannot retrieve it later. You can rotate it from the Platform UI, but the old secret is invalidated.\n\n### Step 3 — Create a Personal Access Token (PAT)\n\nThe plugin authenticates as a user via a **Personal Access Token**.\n\n1. In the **Airlock Platform App** (Settings → Access Tokens) or the **Mobile Approver** app, create a new token.\n2. Set an appropriate expiry (max 1 year).\n3. Copy the token (prefixed with `airpat_`).\n\nThe token identifies which user the enforcer acts on behalf of.\n\n### Step 4 — Generate a Pre-Generated Pairing Code\n\nThe OpenClaw plugin uses **pre-generated pairing codes** (approver-initiated) instead of interactive pairing. This means the approver creates the code first, and you paste it into the plugin config.\n\n**From the Mobile Approver app:**\n\n1. Open the **Airlock** mobile app on your phone.\n2. Tap the **\"+\"** button or go to **Workspaces → Add Workspace**.\n3. Select **\"Generate Code\"** (pre-generated pairing).\n4. The app shows a **6-character pairing code** (e.g. `A3K9X2`).\n5. Copy or note this code — you'll paste it into your plugin config.\n\n> ⏱️ **Time limit**: Pre-generated codes expire after **30 minutes**. Complete plugin setup within this window.\n\nThe code is in **Pending** state until an enforcer claims it. You can see its status in the mobile app.\n\n### Step 5 — Install the Plugin\n\n**From npm** (recommended):\n\n```bash\nnpm install @airlockapp/openclaw-airlock\n```\n\n**From your OpenClaw workspace:**\n\n```bash\nopenclaw plugins install @airlockapp/openclaw-airlock\n```\n\n**From source** (for development):\n\n```bash\ncd gateway_sdk/src/openclaw-airlock\nnpm install\nnpm run build\n```\n\n### Step 6 — Configure the Plugin\n\nAdd to your OpenClaw plugin config:\n\n```json\n{\n  \"plugins\": {\n    \"entries\": {\n      \"airlock\": {\n        \"enabled\": true,\n        \"config\": {\n          \"gatewayUrl\": \"https://igw.airlocks.io\",\n          \"enforcerId\": \"my-enforcer-001\",\n          \"pat\": \"airpat_your_token_here\",\n          \"clientId\": \"ABCDEFGHJKLMNPRSTUVWXYZabc\",\n          \"clientSecret\": \"abcdEFGH1234567890abcdEFGH1234567890abcd\",\n          \"pairingCode\": \"A3K9X2\",\n          \"workspaceName\": \"My AI Workspace\",\n          \"failMode\": \"closed\",\n          \"protectedTools\": [\"exec\", \"shell.*\", \"deploy.run\", \"*\"],\n          \"timeoutMs\": 300000,\n          \"executionMode\": \"poll\"\n        }\n      }\n    }\n  }\n}\n```\n\n### Config Reference\n\n| Field | Required | Default | Description |\n|-------|----------|---------|-------------|\n| `gatewayUrl` | ✓ | — | Airlock Integrations Gateway URL (use `https://igw.airlocks.io` for production) |\n| `enforcerId` | ✓ | — | Unique enforcer instance identifier (your choice, e.g. `oc-prod-001`) |\n| `pat` | ✓* | — | Personal Access Token (`airpat_...`) |\n| `clientId` | ✓* | — | Enforcer App Client ID (from Step 2) |\n| `clientSecret` | ✓* | — | Enforcer App Client Secret (from Step 2) |\n| `pairingCode` | — | — | Pre-generated pairing code from mobile app (from Step 4) |\n| `workspaceName` | — | \"OpenClaw Workspace\" | Human-readable workspace name shown in mobile app |\n| `timeoutMs` | — | 300000 | Approval timeout in ms (default: 5 min) |\n| `pollIntervalMs` | — | 3000 | Decision poll interval in ms (min 1000) |\n| `failMode` | — | \"closed\" | `\"open\"` = allow on timeout, `\"closed\"` = block |\n| `protectedTools` | — | [] | Tool names requiring approval (empty = none) |\n| `executionMode` | — | \"poll\" | `\"poll\"` (only mode available) |\n\n> *`pat` is required for user identity. At least `clientId` **or** `pat` is required for authentication.\n\n### Step 7 — Pair and Verify\n\nOnce configured, run the setup and pairing commands:\n\n```bash\n# Validate config and test gateway connectivity\nopenclaw airlock setup\n\n# Claim the pre-generated pairing code (X25519 ECDH key exchange)\nopenclaw airlock pair\n\n# Check everything is connected\n/airlock-status\n```\n\nThe `airlock pair` command:\n1. Generates an X25519 keypair for end-to-end encryption\n2. Claims the pre-generated code with the gateway\n3. Polls until the mobile app completes the pairing\n4. Derives the shared AES-256-GCM encryption key via ECDH + HKDF-SHA256\n5. Stores the approver's Ed25519 public key for decision verification\n6. Persists all state to `.airlock/pairing-state.json`\n\n> 🔒 The `.airlock/` directory is automatically gitignored. It contains secrets (encryption key, routing token) that must not be committed.\n\n### Step 8 — You're Ready!\n\nAfter pairing, the plugin is fully operational:\n\n- **Protected tools** are automatically intercepted and require mobile approval\n- **Presence heartbeat** keeps the mobile app showing your workspace as online\n- **State persists** across restarts — no need to re-pair\n\n---\n\n## Usage\n\n### Automatic Enforcement (Hook)\n\nConfigure `protectedTools` to automatically intercept tool executions:\n\n```json\n{\n  \"protectedTools\": [\"shell.exec\", \"deploy.run\", \"database.query\"]\n}\n```\n\nAny tool matching these names will require mobile approval before executing. Supports glob patterns (e.g. `shell.*` matches `shell.exec`, `shell.run`).\n\nWhen a DND (Do Not Disturb) policy is active, protected tools are auto-approved silently.\n\n### Explicit Approval (Tool)\n\nThe AI agent can explicitly request approval using the `airlock_request_approval` tool:\n\n```\nAction: deploy to production\nReason: User requested deployment of v2.1.0\n```\n\nThis tool also respects DND policies.\n\n### Status Check (Tool)\n\nCheck the status of a previous approval request using `airlock_check_status`:\n\n```\nRequest ID: req-abc123\n```\n\n### Slash Command\n\nUse `/airlock-status` in chat to see the current status including:\n- Gateway connectivity\n- Pairing status and state file info\n- Encryption key availability\n- Protected tools list\n\n---\n\n## Fail Modes\n\n| Scenario | `failMode: \"closed\"` | `failMode: \"open\"` |\n|----------|----------------------|---------------------|\n| Approval timeout | ✗ Block | ✓ Allow |\n| Gateway unreachable | ✗ Block | ✓ Allow |\n| Network error | ✗ Block | ✓ Allow |\n| Pairing revoked | ✗ Block (always) | ✗ Block (always) |\n| No approver (stale) | ✗ Block (always) | ✗ Block (always) |\n| Quota exceeded | ✗ Block (always) | ✗ Block (always) |\n\n---\n\n## Security\n\n### End-to-End Encryption\n\nAll approval requests are encrypted using AES-256-GCM. The encryption key is derived via:\n1. X25519 ECDH key exchange during pairing\n2. HKDF-SHA256 key derivation with info `\"HARP-E2E-AES256GCM\"`\n\nThe gateway never sees the plaintext — it only routes encrypted artifacts.\n\n### Decision Signature Verification\n\nApprover decisions are signed with Ed25519. The plugin verifies signatures using the approver's public key stored during pairing. Canonical format: `${artifactHash}|${decision}|${nonce}`. Unsigned decisions are accepted; decisions with invalid signatures are rejected.\n\n### State Storage\n\n| Data | Location |\n|------|----------|\n| Routing token | `.airlock/pairing-state.json` |\n| Encryption key (AES-256-GCM) | `.airlock/pairing-state.json` |\n| Approver public keys | `.airlock/pairing-state.json` |\n| Pairing timestamp | `.airlock/pairing-state.json` |\n\n> ⚠️ **Security**: The `.airlock/` directory is gitignored by default. If you move or copy the project, ensure the state file is protected and not committed to version control.\n\n---\n\n## Troubleshooting\n\n### \"Not paired — run 'airlock pair' first\"\n- Make sure `pairingCode` is set in config.\n- Generate a new code if the previous one expired (30-min TTL).\n- Run `openclaw airlock pair`.\n\n### \"No approver available — pairing may be stale\"\n- The pairing was revoked from the mobile app.\n- Generate a new pre-generated code and run pair again.\n\n### \"Access denied: pairing_revoked\"\n- The mobile app user revoked the pairing.\n- The plugin automatically clears stale pairing state.\n- Generate a new code and run `openclaw airlock pair`.\n\n### \"Approval timed out\"\n- The approver didn't respond within the timeout period.\n- Increase `timeoutMs` if needed, or check if the approver's mobile app is reachable.\n\n### Plugin not intercepting tools\n- Check that `protectedTools` includes the tool names you want to gate.\n- An empty `protectedTools` array means no automatic enforcement (opt-in model).\n- OpenClaw's built-in shell/bash execution tool is named **`exec`** internally — not `bash` or `shell.exec`. Add `\"exec\"` to `protectedTools`, or use `\"*\"` to intercept all tools regardless of name.\n- Use the `airlock_request_approval` tool for explicit control.\n\n### Gateway unreachable\n- Verify the `gatewayUrl` is correct (`https://igw.airlocks.io` for production).\n- Run `openclaw airlock setup` to test connectivity.\n- Check network/firewall settings.\n\n---\n\n## Development\n\n```bash\nnpm install\nnpm run build        # Compile TypeScript\nnpm run dev          # Watch mode\nnpm run typecheck    # Type-check without emitting\n```\n\n### Project Structure\n\n```\ngateway_sdk/src/openclaw-airlock/\n  openclaw.plugin.json          # OpenClaw manifest + config schema\n  package.json                  # @airlockapp/openclaw-airlock npm package\n  tsconfig.json                 # TypeScript ESM config\n  .gitignore                    # Excludes .airlock/, dist/, node_modules/\n\n  src/\n    index.ts                    # Plugin entry — registers all capabilities\n    config.ts                   # Config parsing, validation, defaults\n    client.ts                   # Gateway SDK wrapper: approval, pairing, heartbeat, DND\n    crypto.ts                   # X25519 ECDH key exchange + Ed25519 signature verification\n    state.ts                    # Pairing state persistence (.airlock/pairing-state.json)\n\n    tools/\n      requestApproval.ts        # AI-callable tool: explicit approval request\n      checkStatus.ts            # AI-callable tool: check exchange status\n\n    hooks/\n      beforeTool.ts             # Automatic tool interception hook (+ DND check)\n\n    commands/\n      airlockStatus.ts          # /airlock-status diagnostic command\n\n    cli/\n      setup.ts                  # `airlock setup` — validate config + connectivity\n      pair.ts                   # `airlock pair` — claim pre-generated code\n```\n\n---\n\n## Architecture\n\n```\n┌───────────────────┐    HTTPS     ┌──────────────────────────┐\n│  OpenClaw Agent    │ ──────────→ │  Integrations Gateway     │\n│  + Airlock Plugin  │ ←────────── │  (igw.airlocks.io)        │\n└───────────────────┘              └────────────┬─────────────┘\n                                                │\n                                     Internal routing\n                                                │\n                                   ┌────────────▼─────────────┐\n                                   │  Airlock Platform Backend │\n                                   └────────────┬─────────────┘\n                                                │\n                                     Push notification\n                                                │\n                                   ┌────────────▼─────────────┐\n                                   │  Mobile Approver App      │\n                                   │  (approve / deny)         │\n                                   └──────────────────────────┘\n```\n\nKey components:\n\n- **@airlockapp/gateway-sdk** — TypeScript SDK for the Airlock Integrations Gateway API\n- **X25519 ECDH** — Key exchange for end-to-end encryption (AES-256-GCM)\n- **Ed25519** — Decision signature verification\n- **PAT + Client Credentials** — Dual authentication (user identity + app identity)\n- **Pre-generated pairing codes** — Config-driven pairing (no interactive browser login)\n- **Long-polling** — Server-side 25s timeout for efficient decision waiting\n- **Presence heartbeat** — 45s interval for online/offline status in mobile app\n- **DND policy check** — Auto-approval when Do Not Disturb is active\n\n---\n\n## Further Reading\n\n- [Airlock Developer Guide](https://airlockapp.io/docs/developer-guide/) — Developer programme, enforcer lifecycle, API reference\n- [Gateway Client SDKs](https://airlockapp.io/docs/sdk/) — Published SDK packages and documentation\n- [Gateway SDK (TypeScript)](../typescript/README.md) — TypeScript SDK documentation\n- [HARP Specification](../../../harp-spec/) — Human-Approvable Request Protocol specification\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}