@lifeaitools/clauth 1.30.6 → 1.30.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.clauth-skill/SKILL.md +17 -75
- package/README.md +10 -70
- package/cli/api.js +11 -110
- package/cli/commands/scrub.js +109 -205
- package/cli/commands/serve.js +850 -3059
- package/cli/fingerprint.js +10 -0
- package/cli/index.js +58 -22
- package/cli/studio-debug.js +8 -679
- package/cli/supervisor-registry.js +440 -0
- package/cli/supervisor-registry.test.js +173 -0
- package/package.json +3 -10
- package/scripts/bin/bootstrap-linux +0 -0
- package/scripts/bin/bootstrap-macos +0 -0
- package/scripts/bin/bootstrap-win.exe +0 -0
- package/scripts/postinstall.js +0 -25
- package/cli/api.classify.test.js +0 -75
- package/cli/commands/agent-cron.js +0 -396
- package/cli/commands/agent-pool.js +0 -1962
- package/cli/commands/scrub.test.js +0 -115
- package/cli/enrollment-script.js +0 -82
- package/cli/webdav-service.js +0 -339
package/.clauth-skill/SKILL.md
CHANGED
|
@@ -11,47 +11,23 @@ Hardware-bound credential vault for the LIFEAI stack. Your machine is the second
|
|
|
11
11
|
|
|
12
12
|
## How Claude Interfaces with clauth
|
|
13
13
|
|
|
14
|
-
> **CRITICAL:**
|
|
15
|
-
> exact secret retrieval. Do not ask the user for the vault password just to get
|
|
16
|
-
> a key, do not use interactive prompts, and do not walk every service.
|
|
14
|
+
> **CRITICAL:** Never pipe input to clauth or use interactive prompts. The CLI uses `inquirer` which produces ANSI garbage when stdin is piped. Always use one of the two methods below.
|
|
17
15
|
|
|
18
|
-
### Method 1 —
|
|
16
|
+
### Method 1 — CLI with `--pw` flag (simplest)
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
curl -s http://127.0.0.1:52437/ping
|
|
24
|
-
curl -s http://127.0.0.1:52437/knowledge
|
|
25
|
-
curl -s http://127.0.0.1:52437/list-services
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Retrieve exactly one credential only when the next command needs it:
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
curl -s http://127.0.0.1:52437/v/<service>
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
`/knowledge` is safe to cache for a session: it returns service metadata and
|
|
35
|
-
`has_key`, but no raw secrets and no Vault secret names. `/status` uses the same
|
|
36
|
-
short metadata cache and can be forced with `?refresh=1`.
|
|
37
|
-
|
|
38
|
-
### Method 2 — CLI with `--pw` flag (admin/setup only)
|
|
39
|
-
|
|
40
|
-
All clauth commands accept `-p` / `--pw <password>` to skip the interactive
|
|
41
|
-
password prompt. Use this for local admin/setup tasks, not routine agent secret
|
|
42
|
-
retrieval:
|
|
18
|
+
All clauth commands accept `-p` / `--pw <password>` to skip the interactive password prompt:
|
|
43
19
|
|
|
44
20
|
```bash
|
|
45
21
|
clauth status -p "YourPassword"
|
|
22
|
+
clauth get github -p "YourPassword"
|
|
46
23
|
clauth test -p "YourPassword"
|
|
47
24
|
clauth enable github -p "YourPassword"
|
|
48
25
|
clauth write key github -p "YourPassword" # still prompts for the key value
|
|
49
26
|
```
|
|
50
27
|
|
|
51
|
-
### Method
|
|
28
|
+
### Method 2 — Direct API calls (full control, no CLI needed)
|
|
52
29
|
|
|
53
|
-
Call the auth-vault Edge Function directly
|
|
54
|
-
Normal agents should not bypass the daemon metadata cache.
|
|
30
|
+
Call the auth-vault Edge Function directly. This is the most reliable method for Claude.
|
|
55
31
|
|
|
56
32
|
**Base URL:** `https://<project-ref>.supabase.co/functions/v1/auth-vault`
|
|
57
33
|
**Auth header:** `Authorization: Bearer <supabase-anon-key>`
|
|
@@ -99,10 +75,10 @@ All auth routes require: `machine_hash`, `token`, `timestamp`, `password`.
|
|
|
99
75
|
|
|
100
76
|
### Password Handling
|
|
101
77
|
|
|
102
|
-
-
|
|
103
|
-
-
|
|
78
|
+
- Ask the user for their clauth password **once per session**
|
|
79
|
+
- Store it in working memory for the duration of the conversation
|
|
104
80
|
- Never log or echo the password
|
|
105
|
-
- If the
|
|
81
|
+
- If the user says "use clauth" or "get my github key", ask for the password if you don't have it yet
|
|
106
82
|
|
|
107
83
|
---
|
|
108
84
|
|
|
@@ -168,9 +144,7 @@ clauth setup [--admin-token T] [-p P] Register this machine
|
|
|
168
144
|
clauth status [-p P] All services + state
|
|
169
145
|
clauth test [-p P] Verify HMAC connection
|
|
170
146
|
clauth list [-p P] Service names
|
|
171
|
-
clauth search <query> [-p P] Search names and
|
|
172
|
-
clauth search <query> --addresses [-p P]
|
|
173
|
-
Also search redacted address hints (may read multiple secrets)
|
|
147
|
+
clauth search <query> [-p P] Search names, metadata, and redacted server addresses
|
|
174
148
|
|
|
175
149
|
clauth write key <service> [-p P] Store a credential
|
|
176
150
|
clauth write pw [-p P] Change password
|
|
@@ -201,64 +175,32 @@ Service type `fileserver` — mount configuration for fs tools (UI-only config).
|
|
|
201
175
|
|
|
202
176
|
---
|
|
203
177
|
|
|
204
|
-
##
|
|
205
|
-
|
|
206
|
-
When discovering service names, do not retrieve secrets. Use the daemon metadata
|
|
207
|
-
cache:
|
|
208
|
-
|
|
209
|
-
```bash
|
|
210
|
-
curl -s http://127.0.0.1:52437/knowledge
|
|
211
|
-
curl -s http://127.0.0.1:52437/list-services
|
|
212
|
-
```
|
|
178
|
+
## MCP Server (v1.5.38+)
|
|
213
179
|
|
|
214
|
-
|
|
215
|
-
raw secrets and no Vault secret names. Fetch `GET /v/<service>` only for the one
|
|
216
|
-
exact credential needed by the next command. Never walk every service and never
|
|
217
|
-
bulk-fetch all secrets.
|
|
218
|
-
|
|
219
|
-
## MCP Server (v1.16.9+)
|
|
220
|
-
|
|
221
|
-
clauth runs as an MCP server with five namespaced paths and safe
|
|
222
|
-
credential-discovery tools:
|
|
180
|
+
clauth runs as an MCP server with 3 namespaces and 27 tools:
|
|
223
181
|
|
|
224
182
|
| Path | Namespace | Tools |
|
|
225
183
|
|------|-----------|-------|
|
|
226
|
-
| `/clauth` | `clauth_*`
|
|
184
|
+
| `/clauth` | `clauth_*` | 13 credential vault tools |
|
|
227
185
|
| `/gws` | `gws_*` | 6 Google Workspace tools |
|
|
228
|
-
| `/fs` | `fs_*` |
|
|
229
|
-
| `/
|
|
230
|
-
| `/codevelop` | `codevelop_*` | 7 peer development tools |
|
|
231
|
-
| `/mcp` | all | 63 tools combined |
|
|
232
|
-
|
|
233
|
-
The default live `/clauth` surface includes credential tools plus
|
|
234
|
-
`call_agent`, `monkey_dispatch`, `handoff_start`, `terminal_*`, and `channel_*`.
|
|
235
|
-
Admin write tools are gated by write-mode.
|
|
186
|
+
| `/fs` | `fs_*` | 8 filesystem tools (read, write, grep, glob, list, delete, mkdir, mounts) |
|
|
187
|
+
| `/mcp` | all | 27 tools combined |
|
|
236
188
|
|
|
237
189
|
### claude.ai Connector URLs (noauth mode)
|
|
238
190
|
- `https://clauth.regendevcorp.com/clauth` — credential tools
|
|
239
191
|
- `https://clauth.regendevcorp.com/gws` — Google Workspace
|
|
240
|
-
- `https://clauth.regendevcorp.com/chitchat` — collaboration relay tools
|
|
241
|
-
- `https://clauth.regendevcorp.com/codevelop` — peer development tools
|
|
242
192
|
- `https://fs.regendevcorp.com/fs` — filesystem tools
|
|
243
193
|
|
|
244
194
|
Noauth mode: fresh domains that return 404 on OAuth endpoints. claude.ai connects directly (Anthropic OAuth proxy bug workaround).
|
|
245
195
|
|
|
246
|
-
Use `clauth_knowledge` or `clauth_status` for discovery. Use `clauth_get` only
|
|
247
|
-
for one exact secret. `clauth_search` is metadata-only by default; pass
|
|
248
|
-
`addresses: true` only when redacted address hints are intentionally needed.
|
|
249
|
-
`clauth_inject` is guarded against accidental bulk vault sweeps.
|
|
250
|
-
|
|
251
196
|
### FS Tools
|
|
252
|
-
`fs_read` `fs_write` `
|
|
253
|
-
`fs_import_git_files` `fs_list` `fs_grep` `fs_glob` `fs_delete` `fs_mkdir`
|
|
254
|
-
`fs_edit` `fs_move` `fs_copy` `fs_mounts` `fs_repo_status` `fs_use_branch`
|
|
255
|
-
`fs_commit` `fs_diff`
|
|
197
|
+
`fs_read` `fs_write` `fs_list` `fs_grep` `fs_glob` `fs_delete` `fs_mkdir` `fs_mounts`
|
|
256
198
|
|
|
257
199
|
Path-jail security: all paths resolved against mount root. Permission flags (r/w/d) per mount. Uses `@vscode/ripgrep` for grep, `fast-glob` for glob.
|
|
258
200
|
|
|
259
201
|
### Testing
|
|
260
202
|
```bash
|
|
261
|
-
node test-tools.mjs #
|
|
203
|
+
node test-tools.mjs # 25 tool execution tests across all 3 namespaces
|
|
262
204
|
```
|
|
263
205
|
|
|
264
206
|
---
|
package/README.md
CHANGED
|
@@ -92,9 +92,7 @@ clauth install Provision Supabase + install Claude skill
|
|
|
92
92
|
clauth setup Register this machine with the vault
|
|
93
93
|
clauth enroll Create one-time code to add another computer
|
|
94
94
|
clauth status All services + state
|
|
95
|
-
clauth search <query> Find services by name, project, description, or
|
|
96
|
-
clauth search <query> --addresses
|
|
97
|
-
Also search redacted address hints (may read multiple secrets)
|
|
95
|
+
clauth search <query> Find services by name, project, description, or redacted address
|
|
98
96
|
clauth test Verify connection
|
|
99
97
|
|
|
100
98
|
clauth write key <service> Store a credential
|
|
@@ -138,7 +136,7 @@ Nothing stored locally. Password never persisted. Machine hash is one-way only.
|
|
|
138
136
|
clauth runs as an HTTP daemon on `http://127.0.0.1:52437`. The daemon provides:
|
|
139
137
|
|
|
140
138
|
- **Web UI** — unlock vault, manage services, configure mounts
|
|
141
|
-
- **REST API** — `GET /
|
|
139
|
+
- **REST API** — `GET /get/<service>`, `GET /ping`, `POST /restart`, `GET /shutdown`
|
|
142
140
|
- **MCP server** — Model Context Protocol for Claude Code and claude.ai
|
|
143
141
|
- **Cloudflare Tunnel** — exposes MCP endpoints publicly for claude.ai connectors
|
|
144
142
|
|
|
@@ -146,88 +144,32 @@ Start: `clauth serve start` (starts locked, auto-opens browser for unlock).
|
|
|
146
144
|
|
|
147
145
|
Full daemon operations reference: see `regen-root/.claude/rules/clauth.md`.
|
|
148
146
|
|
|
149
|
-
### Safe Metadata Cache
|
|
150
|
-
|
|
151
|
-
Agents and scripts must discover services through metadata endpoints, not by
|
|
152
|
-
walking every secret endpoint. The daemon caches service metadata for 30 seconds:
|
|
153
|
-
|
|
154
|
-
```bash
|
|
155
|
-
curl -s http://127.0.0.1:52437/knowledge # sanitized, agent-safe metadata
|
|
156
|
-
curl -s http://127.0.0.1:52437/list-services # names only
|
|
157
|
-
curl -s http://127.0.0.1:52437/status # full daemon metadata, cached
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
`/knowledge` returns service names, labels, key types, enabled state, project,
|
|
161
|
-
description, timestamps, and `has_key`. It never returns raw secrets or Vault
|
|
162
|
-
secret names. Use `GET /v/<service>` only for the one exact secret needed by the
|
|
163
|
-
next command. Do not bulk-fetch every service.
|
|
164
|
-
|
|
165
147
|
---
|
|
166
148
|
|
|
167
|
-
## MCP Server —
|
|
149
|
+
## MCP Server — 3 Namespaces, 32 Tools
|
|
168
150
|
|
|
169
151
|
clauth is the single MCP interface for all local tools. One process, namespaced paths:
|
|
170
152
|
|
|
171
153
|
| Path | Namespace | Tools | Description |
|
|
172
154
|
|------|-----------|-------|-------------|
|
|
173
|
-
| `/clauth` | `clauth_*`
|
|
155
|
+
| `/clauth` | `clauth_*` | 13 | Credential vault operations |
|
|
174
156
|
| `/gws` | `gws_*` | 6 | Google Workspace (Gmail, Calendar, Drive) |
|
|
175
|
-
| `/fs` | `fs_*` |
|
|
176
|
-
| `/
|
|
177
|
-
| `/codevelop` | `codevelop_*` | 7 | Peer development sessions |
|
|
178
|
-
| `/mcp` | all | 63 | All namespaces combined (Claude Code) |
|
|
179
|
-
|
|
180
|
-
The default live `/clauth` surface includes 13 credential tools plus
|
|
181
|
-
`call_agent`, `monkey_dispatch`, `handoff_start`, `terminal_*`, and `channel_*`
|
|
182
|
-
runtime helpers. Admin write tools such as enable/disable/project/token actions
|
|
183
|
-
are gated by write-mode.
|
|
184
|
-
|
|
185
|
-
### TinTin Agent Setup
|
|
186
|
-
|
|
187
|
-
clauth owns the local TinTin settings and isolated agent setup contract used by
|
|
188
|
-
app sidebars and the standalone TinTin console.
|
|
189
|
-
|
|
190
|
-
| Method | Route | Purpose |
|
|
191
|
-
| --- | --- | --- |
|
|
192
|
-
| `GET` | `/tintin/settings` | Read persisted TinTin app/agent/sidebar/dispatch/co-develop settings. |
|
|
193
|
-
| `PUT` | `/tintin/settings` | Save TinTin app/agent/sidebar/dispatch/co-develop settings. |
|
|
194
|
-
| `GET` | `/tintin/settings/ui` | Browser settings page for operators, with sections for Agent Setup, Agent Defaults, Sidebar Apps, Blackboard Dispatch, Co-develop Relay, and stored agent sessions. |
|
|
195
|
-
| `POST` | `/tintin/agent-sessions` | Create or reuse an isolated agent cwd, normally a git worktree. |
|
|
196
|
-
|
|
197
|
-
The agent-session response includes the requested agent/runtime/model, repo
|
|
198
|
-
root, isolated `cwd`, worktree path, branch, launch mode, and launch command.
|
|
199
|
-
This route prepares the proper Codex/Claude isolation path; separate supervisor
|
|
200
|
-
code is still responsible for long-running process parking and wake/resume.
|
|
201
|
-
|
|
202
|
-
### clauth MCP Usage
|
|
203
|
-
|
|
204
|
-
Use `clauth_knowledge` or `clauth_status` for discovery. They use cached
|
|
205
|
-
metadata and never retrieve raw secrets. `clauth_search` is metadata-only by
|
|
206
|
-
default; pass `addresses: true` only when you intentionally need redacted address
|
|
207
|
-
hints from connstrings/fileserver/oauth secrets. `clauth_get` is for one exact
|
|
208
|
-
secret. `clauth_inject` refuses more than five services unless `allow_many: true`
|
|
209
|
-
is explicitly supplied, to prevent accidental rate-limit lockouts from bulk
|
|
210
|
-
secret sweeps.
|
|
157
|
+
| `/fs` | `fs_*` | 13 | Filesystem (read, write, append, chunked write, URL ingest, Git import, stat, grep, glob, delete, mkdir, mounts) |
|
|
158
|
+
| `/mcp` | all | 32 | All namespaces combined (Claude Code) |
|
|
211
159
|
|
|
212
160
|
### FS Tools
|
|
213
161
|
|
|
214
|
-
|
|
215
|
-
- `fs_read`, `fs_write`, `fs_stat`, `fs_append`, `fs_write_chunk`, `fs_ingest_url`, `fs_import_git_files`, `fs_list`, `fs_grep`, `fs_glob`, `fs_delete`, `fs_mkdir`, `
|
|
162
|
+
13 filesystem tools with path-jail security:
|
|
163
|
+
- `fs_read`, `fs_write`, `fs_stat`, `fs_append`, `fs_write_chunk`, `fs_ingest_url`, `fs_import_git_files`, `fs_list`, `fs_grep`, `fs_glob`, `fs_delete`, `fs_mkdir`, `fs_mounts`
|
|
216
164
|
- Uses `node:fs/promises` (async), `@vscode/ripgrep` (shipped binary), `fast-glob`
|
|
217
165
|
- Permission flags per mount: `r` (read), `w` (write), `d` (delete)
|
|
218
166
|
- Mount config stored as "fileserver" service type in vault — only configurable through web UI
|
|
219
167
|
- Large writes should use `fs_write_chunk`; cloud-to-local transfer should use `fs_ingest_url`; guarded appends should pass `expected_sha256` from `fs_stat`
|
|
220
168
|
- Durable new files authored by Claude.ai in GitHub should use `fs_import_git_files` so the local dirty monorepo fetches and restores only named paths without `git pull`
|
|
221
169
|
|
|
222
|
-
### Collaboration Tools
|
|
223
|
-
|
|
224
|
-
Chitchat tools: `chitchat_start`, `chitchat_list`, `chitchat_send`, `chitchat_recv`, `chitchat_reply`, `chitchat_poll`, `chitchat_stop`
|
|
225
|
-
|
|
226
|
-
Codevelop tools: `codevelop_start`, `codevelop_join`, `codevelop_send`, `codevelop_poll`, `codevelop_status`, `codevelop_stream`, `codevelop_stop`
|
|
227
|
-
|
|
228
170
|
### GWS Tools
|
|
229
171
|
|
|
230
|
-
6 Google Workspace tools: `
|
|
172
|
+
6 Google Workspace tools: `gws_gmail_list`, `gws_gmail_read`, `gws_gmail_send`, `gws_gmail_draft`, `gws_calendar_list`, `gws_calendar_create`
|
|
231
173
|
- Calls `gws` CLI via `execSync` with `shell: 'bash'` (fixes Windows cmd.exe JSON quoting)
|
|
232
174
|
|
|
233
175
|
---
|
|
@@ -254,8 +196,6 @@ Full OAuth 2.1 protocol implementation is present for future use when Anthropic
|
|
|
254
196
|
|-----------|-----|
|
|
255
197
|
| clauth | `https://clauth.regendevcorp.com/clauth` |
|
|
256
198
|
| gws | `https://clauth.regendevcorp.com/gws` |
|
|
257
|
-
| chitchat | `https://clauth.regendevcorp.com/chitchat` |
|
|
258
|
-
| codevelop | `https://clauth.regendevcorp.com/codevelop` |
|
|
259
199
|
| fs | `https://fs.regendevcorp.com/fs` |
|
|
260
200
|
|
|
261
201
|
---
|
|
@@ -270,7 +210,7 @@ Full OAuth 2.1 protocol implementation is present for future use when Anthropic
|
|
|
270
210
|
## Testing
|
|
271
211
|
|
|
272
212
|
```bash
|
|
273
|
-
node test-tools.mjs #
|
|
213
|
+
node test-tools.mjs # 25 tool execution tests across all 3 namespaces
|
|
274
214
|
```
|
|
275
215
|
|
|
276
216
|
Tests actual MCP tool calls (not just OAuth + listing).
|
package/cli/api.js
CHANGED
|
@@ -27,119 +27,21 @@ export function getAnonKey() {
|
|
|
27
27
|
// ============================================================
|
|
28
28
|
// Core POST helper
|
|
29
29
|
// ============================================================
|
|
30
|
-
// Bound every vault round-trip with a wall-clock timeout. Without this, a TCP
|
|
31
|
-
// stall (DNS black-hole, half-open socket — NOT a clean 4xx) hangs the caller
|
|
32
|
-
// forever. Callers that treat persistence as best-effort (e.g. the call_agent
|
|
33
|
-
// scratchpad mirror) rely on this resolving to an error rather than blocking.
|
|
34
|
-
// Override via CLAUTH_VAULT_TIMEOUT_MS; default 8s.
|
|
35
|
-
const VAULT_FETCH_TIMEOUT_MS = (() => {
|
|
36
|
-
const n = Number(process.env.CLAUTH_VAULT_TIMEOUT_MS);
|
|
37
|
-
return Number.isFinite(n) && n > 0 ? n : 8000;
|
|
38
|
-
})();
|
|
39
|
-
|
|
40
|
-
// ============================================================
|
|
41
|
-
// Backend / external-resource error model
|
|
42
|
-
// ============================================================
|
|
43
|
-
// A VaultBackendError means the vault backend (the auth-vault Edge Function or
|
|
44
|
-
// the Postgres database behind it) never rendered an auth verdict — the request
|
|
45
|
-
// timed out, the network failed, the function 5xx'd, the DB was unreachable, or
|
|
46
|
-
// we were rate-limited. These are TRANSIENT and must NEVER be counted as an
|
|
47
|
-
// authentication failure (a wrong password). Treating a database timeout as a
|
|
48
|
-
// bad password is the bug that turned a DB blip into a permanent vault lockout.
|
|
49
|
-
export class VaultBackendError extends Error {
|
|
50
|
-
constructor(kind, message, { status = null, detail = null, cause = null } = {}) {
|
|
51
|
-
super(message);
|
|
52
|
-
this.name = "VaultBackendError";
|
|
53
|
-
this.kind = kind; // timeout | network | server_error | rate_limited | db_error | unknown
|
|
54
|
-
this.isBackend = true; // marker for callers: do not strike
|
|
55
|
-
this.retriable = true; // the verdict was never rendered — safe to retry
|
|
56
|
-
this.status = status; // HTTP status if the server responded
|
|
57
|
-
this.detail = detail; // human-readable backend detail
|
|
58
|
-
if (cause) this.cause = cause;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Server `reason` strings that are genuine AUTH VERDICTS (the backend evaluated
|
|
63
|
-
// the credential and answered). These DO count — they are not backend errors.
|
|
64
|
-
const AUTH_VERDICT_REASONS = [
|
|
65
|
-
"invalid_token", "wrong_password", "invalid_password",
|
|
66
|
-
"machine_locked", "machine_disabled", "machine_not_found",
|
|
67
|
-
];
|
|
68
|
-
|
|
69
|
-
// Classify a server-supplied `reason` string. Returns a VaultBackendError kind
|
|
70
|
-
// when the reason describes a backend/transient condition, or null when the
|
|
71
|
-
// reason is a real auth verdict (or unknown — caller decides).
|
|
72
|
-
export function classifyServerReason(reason) {
|
|
73
|
-
if (!reason || typeof reason !== "string") return null;
|
|
74
|
-
const r = reason.toLowerCase();
|
|
75
|
-
if (AUTH_VERDICT_REASONS.some((v) => r.includes(v))) return null; // genuine verdict
|
|
76
|
-
if (/rate[_\s-]?limit/.test(r)) return "rate_limited";
|
|
77
|
-
if (/(database|\bdb\b|postgres|connection|pool|statement timeout|unavailable|internal|timeout|5\d\d)/.test(r)) return "db_error";
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Classify a thrown error (transport/timeout/network/typed). Returns a kind
|
|
82
|
-
// string when it is a backend/transient failure, or null when it is not.
|
|
83
|
-
export function classifyBackendError(err) {
|
|
84
|
-
if (!err) return null;
|
|
85
|
-
if (err instanceof VaultBackendError) return err.kind;
|
|
86
|
-
const name = err.name || "";
|
|
87
|
-
const msg = (err.message || "").toString();
|
|
88
|
-
if (name === "TimeoutError" || name === "AbortError" || /\babort(ed)?\b|\btim(e|ed)?\s*out\b|timeout/i.test(msg)) return "timeout";
|
|
89
|
-
if (/fetch failed|ECONNREFUSED|ENOTFOUND|ETIMEDOUT|EAI_AGAIN|ECONNRESET|EPIPE|network|socket hang/i.test(msg)) return "network";
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
30
|
async function post(route, body) {
|
|
94
31
|
const url = `${getBaseUrl()}/${route}`;
|
|
95
32
|
const anonKey = getAnonKey();
|
|
96
33
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
body: JSON.stringify(body),
|
|
106
|
-
signal: AbortSignal.timeout(VAULT_FETCH_TIMEOUT_MS)
|
|
107
|
-
});
|
|
108
|
-
} catch (err) {
|
|
109
|
-
// Transport-level failure — the backend never answered. Surface a typed,
|
|
110
|
-
// non-strike error so callers report "backend unreachable", not "bad password".
|
|
111
|
-
const kind = classifyBackendError(err) || "network";
|
|
112
|
-
const why = kind === "timeout"
|
|
113
|
-
? `vault backend did not respond within ${VAULT_FETCH_TIMEOUT_MS}ms`
|
|
114
|
-
: `cannot reach vault backend (${err.message || "network error"})`;
|
|
115
|
-
throw new VaultBackendError(kind, why, { detail: err.message || String(err), cause: err });
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
let data;
|
|
119
|
-
try {
|
|
120
|
-
data = await res.json();
|
|
121
|
-
} catch (err) {
|
|
122
|
-
// 2xx/5xx with a non-JSON or empty body — the function errored without a
|
|
123
|
-
// structured verdict. Transient backend condition, not an auth failure.
|
|
124
|
-
throw new VaultBackendError("server_error", `vault backend returned an unreadable response (HTTP ${res.status})`, { status: res.status, detail: err.message });
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// HTTP 5xx (or any non-ok with no structured error) = backend problem, never a verdict.
|
|
128
|
-
if (res.status >= 500 || (!res.ok && !data.error)) {
|
|
129
|
-
const reasonKind = classifyServerReason(data.reason || data.error) || "server_error";
|
|
130
|
-
throw new VaultBackendError(reasonKind, `vault backend error (HTTP ${res.status})`, { status: res.status, detail: data.error || data.reason || `HTTP ${res.status}` });
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// 4xx WITH a structured error: could be a real verdict (invalid_token) or a
|
|
134
|
-
// transient backend signal surfaced as 4xx (rate_limited). Promote the latter
|
|
135
|
-
// to a typed backend error; leave genuine verdicts in `data` for the caller.
|
|
136
|
-
if (data.error) {
|
|
137
|
-
const reasonKind = classifyServerReason(data.reason || data.error);
|
|
138
|
-
if (reasonKind) {
|
|
139
|
-
throw new VaultBackendError(reasonKind, `vault backend ${reasonKind.replace("_", " ")}`, { status: res.status, detail: data.error || data.reason });
|
|
140
|
-
}
|
|
141
|
-
}
|
|
34
|
+
const res = await fetch(url, {
|
|
35
|
+
method: "POST",
|
|
36
|
+
headers: {
|
|
37
|
+
"Content-Type": "application/json",
|
|
38
|
+
"Authorization": `Bearer ${anonKey}`
|
|
39
|
+
},
|
|
40
|
+
body: JSON.stringify(body)
|
|
41
|
+
});
|
|
142
42
|
|
|
43
|
+
const data = await res.json();
|
|
44
|
+
if (!res.ok && !data.error) throw new Error(`HTTP ${res.status}`);
|
|
143
45
|
return data;
|
|
144
46
|
}
|
|
145
47
|
|
|
@@ -233,6 +135,5 @@ export async function redeemEnrollment(machineHash, seedHash, label, enrollmentC
|
|
|
233
135
|
|
|
234
136
|
export default {
|
|
235
137
|
retrieve, write, enable, addService, updateService, removeService, revoke,
|
|
236
|
-
status, test, createEnrollment, registerMachine, redeemEnrollment, getBaseUrl, getAnonKey
|
|
237
|
-
VaultBackendError, classifyBackendError, classifyServerReason
|
|
138
|
+
status, test, createEnrollment, registerMachine, redeemEnrollment, getBaseUrl, getAnonKey
|
|
238
139
|
};
|