@openparachute/vault 0.1.0 → 0.2.0
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/CHANGELOG.md +80 -0
- package/CLAUDE.md +2 -2
- package/README.md +289 -44
- package/core/src/core.test.ts +802 -346
- package/core/src/expand.ts +140 -0
- package/core/src/hooks.test.ts +27 -27
- package/core/src/hooks.ts +1 -1
- package/core/src/mcp.ts +102 -39
- package/core/src/notes.ts +82 -4
- package/core/src/obsidian.test.ts +11 -11
- package/core/src/paths.test.ts +46 -46
- package/core/src/schema.ts +18 -2
- package/core/src/store.ts +51 -51
- package/core/src/types.ts +29 -29
- package/core/src/wikilinks.test.ts +61 -61
- package/docs/HTTP_API.md +4 -2
- package/package.json +1 -1
- package/src/auth.test.ts +319 -0
- package/src/backup-launchd.test.ts +90 -0
- package/src/backup-launchd.ts +169 -0
- package/src/backup.test.ts +715 -0
- package/src/backup.ts +699 -0
- package/src/cli.ts +923 -31
- package/src/config.test.ts +173 -0
- package/src/config.ts +345 -15
- package/src/daemon.ts +136 -0
- package/src/doctor.test.ts +356 -0
- package/src/health.test.ts +201 -0
- package/src/health.ts +115 -0
- package/src/launchd.test.ts +91 -0
- package/src/launchd.ts +37 -40
- package/src/mcp-http.ts +1 -1
- package/src/mcp-tools.ts +7 -9
- package/src/oauth.test.ts +289 -8
- package/src/oauth.ts +57 -12
- package/src/published.test.ts +21 -21
- package/src/routes.ts +152 -70
- package/src/routing.test.ts +347 -0
- package/src/routing.ts +365 -0
- package/src/server.ts +7 -278
- package/src/systemd.test.ts +15 -0
- package/src/systemd.ts +18 -11
- package/src/triggers.test.ts +7 -7
- package/src/triggers.ts +6 -6
- package/src/vault-store.ts +20 -3
- package/src/vault.test.ts +356 -262
- package/.claude/settings.local.json +0 -31
- package/.playwright-mcp/console-2026-04-14T04-17-25-395Z.log +0 -2
- package/.playwright-mcp/console-2026-04-14T04-18-11-767Z.log +0 -1
- package/.playwright-mcp/console-2026-04-14T04-19-07-733Z.log +0 -2
- package/.playwright-mcp/console-2026-04-14T04-20-45-440Z.log +0 -2
- package/.playwright-mcp/page-2026-04-14T04-17-25-536Z.yml +0 -1
- package/.playwright-mcp/page-2026-04-14T04-18-11-816Z.yml +0 -1
- package/.playwright-mcp/page-2026-04-14T04-18-31-674Z.yml +0 -211
- package/.playwright-mcp/page-2026-04-14T04-19-07-795Z.yml +0 -59
- package/.playwright-mcp/page-2026-04-14T04-19-36-239Z.yml +0 -232
- package/.playwright-mcp/page-2026-04-14T04-19-58-327Z.yml +0 -182
- package/.playwright-mcp/page-2026-04-14T04-20-10-517Z.yml +0 -91
- package/.playwright-mcp/page-2026-04-14T04-20-14-796Z.yml +0 -70
- package/.playwright-mcp/page-2026-04-14T04-20-45-509Z.yml +0 -59
- package/religions-abrahamic-filter.png +0 -0
- package/religions-buddhism-v2.png +0 -0
- package/religions-buddhism.png +0 -0
- package/religions-final.png +0 -0
- package/religions-v1.png +0 -0
- package/religions-v2.png +0 -0
- package/religions-zen.png +0 -0
- package/web/README.md +0 -73
- package/web/bun.lock +0 -827
- package/web/eslint.config.js +0 -23
- package/web/index.html +0 -15
- package/web/package.json +0 -36
- package/web/public/favicon.svg +0 -1
- package/web/public/icons.svg +0 -24
- package/web/src/App.tsx +0 -149
- package/web/src/Graph.tsx +0 -200
- package/web/src/NoteView.tsx +0 -155
- package/web/src/Sidebar.tsx +0 -186
- package/web/src/api.ts +0 -21
- package/web/src/index.css +0 -50
- package/web/src/main.tsx +0 -10
- package/web/src/types.ts +0 -37
- package/web/src/utils.ts +0 -107
- package/web/tsconfig.app.json +0 -25
- package/web/tsconfig.json +0 -7
- package/web/tsconfig.node.json +0 -24
- package/web/vite.config.ts +0 -15
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Parachute Vault are documented here.
|
|
4
|
+
|
|
5
|
+
This project loosely follows [Keep a Changelog](https://keepachangelog.com) and [Semantic Versioning](https://semver.org).
|
|
6
|
+
|
|
7
|
+
## [0.2.0] — 2026-04-17
|
|
8
|
+
|
|
9
|
+
First tagged public release. Ships the auth, backup, and onboarding surface the project needs for first-wave users.
|
|
10
|
+
|
|
11
|
+
### Authentication
|
|
12
|
+
|
|
13
|
+
- **OAuth 2.1 + PKCE** with Dynamic Client Registration (RFC 7591). Claude Desktop, Parachute Daily, and any OAuth-capable MCP client can connect with no manual token paste — user clicks "Add integration", browser opens to the vault's consent page, done.
|
|
14
|
+
- **Owner password** (bcrypt-hashed, min 12 characters) for the OAuth consent page. Prompt fires at `vault init`; manage later with `parachute vault set-password` / `--clear`.
|
|
15
|
+
- **TOTP 2FA with single-use backup codes**. `parachute vault 2fa enroll` prints a QR and one-time backup codes; `status` / `disable` / `backup-codes` subcommands for lifecycle.
|
|
16
|
+
- **Per-vault OAuth scope** — discovery at `/vaults/{name}/.well-known/oauth-authorization-server` returns vault-scoped endpoints. Tokens minted there authenticate only against that vault.
|
|
17
|
+
- **Cross-vault substitution blocked**: an OAuth code issued for one vault cannot be redeemed at another vault's token endpoint (schema-enforced via a `vault_name` column on `oauth_codes`).
|
|
18
|
+
- **Honest token response**: `/oauth/token` returns `{ access_token, token_type, scope, vault }` so the client knows which vault it just connected to.
|
|
19
|
+
- **Two permission tiers**: `full` (CRUD + delete + token management) and `read` (query / list / find-path / vault-info). Tokens default to `full`; pass `--read` to `tokens create` for read-only.
|
|
20
|
+
- **Token CLI**: `parachute vault tokens` (list), `tokens create [--vault] [--read] [--expires <N{h|d|w|m|y}>] [--label]`, `tokens revoke <id> [--vault]`. Tokens are SHA-256 hashed at rest.
|
|
21
|
+
- **Query-param auth for `/view`**: `?key=pvt_...` works alongside `Authorization: Bearer` and `X-API-Key` headers, convenient for browsers.
|
|
22
|
+
|
|
23
|
+
### Backup
|
|
24
|
+
|
|
25
|
+
- **`parachute vault backup`** — one-shot snapshot: atomic `VACUUM INTO` of every vault's `vault.db`, plus `config.yaml` and each vault's `vault.yaml`, bundled as a timestamped `.tar.gz`. Safe under concurrent reads/writes.
|
|
26
|
+
- **Scheduled runs** via `parachute vault backup --schedule hourly|daily|weekly|manual` (macOS launchd). Linux systemd-timer support is a follow-up; wire cron yourself for now.
|
|
27
|
+
- **`backup status`** shows schedule, last run, destinations, next run, and per-destination tier breakdown.
|
|
28
|
+
- **Tiered (grandfather-father-son) retention**. Default: `daily: 7 / weekly: 4 / monthly: 12 / yearly: null` (unbounded). Set any tier to `0` to disable. Local-timezone bucketing.
|
|
29
|
+
- **Pluggable destinations**. `local` (any filesystem path — iCloud Drive, external disk, rsync/Syncthing folder) ships in 0.2.0. `s3`, `rsync`, and `cloud` destinations designed but not yet implemented.
|
|
30
|
+
- **`vault uninstall` tears down the backup agent too** on macOS, so scheduled backups don't keep firing on a removed install.
|
|
31
|
+
|
|
32
|
+
### Reliability
|
|
33
|
+
|
|
34
|
+
- **`parachute vault doctor`** — diagnostic suite covering server-path pointer, wrapper script, launchd agent (macOS) / systemd service (Linux), bun-on-PATH, MCP entry in `~/.claude.json` (presence + URL port match + reachability), port-collision (free / ours / foreign via `lsof` or `ss`), and — when scheduled backups are configured — backup agent + per-destination writability. Exits 1 on any `fail`.
|
|
35
|
+
- **`vault status`** is healthcheck-aware and reports live daemon state, not just service registration.
|
|
36
|
+
- **`vault restart`** blocks until `/health` returns 200, with a sensible budget and progress indicator.
|
|
37
|
+
- **Path-resilient `start.sh`** — the wrapper launchd/systemd executes embeds an absolute `bun` path + points at `~/.parachute/server-path`, which resolves to the current repo location. Move the repo, re-run `vault init`, and the daemon follows you.
|
|
38
|
+
- **Idempotent `vault init`** — safe to re-run after a folder move or config edit; refreshes the pointer, wrapper, and service registration without touching user data.
|
|
39
|
+
- **Graceful shutdown**: in-flight webhook triggers get a 5 s drain window before the daemon exits on SIGTERM/SIGINT.
|
|
40
|
+
|
|
41
|
+
### Multi-vault
|
|
42
|
+
|
|
43
|
+
- **Public `GET /vaults/list`** — unauthenticated discovery endpoint returning only vault names (no descriptions, timestamps, counts, or keys). Lets a client populate a vault picker before OAuth. Operators who want to hide vault existence can set `discovery: disabled` in `~/.parachute/config.yaml` to make the endpoint return 404.
|
|
44
|
+
- **Single-vault auto-default** — when the server has exactly one vault, the unscoped `/mcp`, `/api/*`, and `/oauth/*` paths transparently resolve to it regardless of its name. A lone vault named `journal` works at `/mcp` with no vault-in-URL needed.
|
|
45
|
+
- **Vault-management CLI**: `parachute vault create <name>`, `list` (alias `ls`), `remove <name> --yes` (alias `rm`).
|
|
46
|
+
- **Automatic `default_vault` management** — `vault create` promotes a new vault to default when none is set or the configured default points at a missing vault. `vault remove` promotes the sole survivor when you delete the default and one vault remains.
|
|
47
|
+
|
|
48
|
+
### Install / uninstall
|
|
49
|
+
|
|
50
|
+
- **`vault uninstall`** — removes the daemon registration, the `start.sh` wrapper, the `~/.parachute/server-path` pointer, and the `parachute-vault` entry in `~/.claude.json`. On macOS, tears down both the main vault agent and the backup agent. Preserves all user data.
|
|
51
|
+
- **`vault uninstall --wipe`** — additionally removes `vaults/`, `.env`, `config.yaml`, `vault.log`, and `vault.err` after a second interactive confirm (default NO).
|
|
52
|
+
- **`vault uninstall --yes --wipe`** — scripted destructive path. Skips both confirms and prints an ISO-timestamped audit line to stdout naming the target paths.
|
|
53
|
+
- **`vault url`** prints the local server URL in a script-friendly form.
|
|
54
|
+
|
|
55
|
+
### API / primitives
|
|
56
|
+
|
|
57
|
+
- **Optimistic concurrency on `update-note`** via an `if_updated_at` parameter. When supplied and it doesn't match the note's current `updated_at`, the update is rejected (MCP: `ConflictError`; HTTP: 409). Batch updates fail fast on the first conflict.
|
|
58
|
+
- **Link expansion on `query-notes`** — new `expand_links` / `expand_depth` (0–3) / `expand_mode` (`"full"` | `"summary"`) parameters inline `[[wikilink]]` targets directly into the returned content. Works on the MCP tool and the HTTP routes (single-note, search, and structured-list).
|
|
59
|
+
- **9 composable MCP tools** (was 30): `query-notes`, `create-note`, `update-note`, `delete-note`, `list-tags`, `update-tag`, `delete-tag`, `find-path`, `vault-info`. Every note parameter accepts either an ID or a path.
|
|
60
|
+
- **Webhook triggers** — declarative config-driven webhooks fire on note mutations matching tag / metadata predicates. Three send modes: `json` (general), `attachment` (Whisper-compatible transcription), `content` (OpenAI-compatible TTS).
|
|
61
|
+
|
|
62
|
+
### Documentation
|
|
63
|
+
|
|
64
|
+
- Entirely overhauled onboarding path: OAuth walkthrough, doctor + troubleshooting, first-run narrative (what `vault init` does on disk), multi-vault subsection, Tailscale Funnel walkthrough, prerequisites block.
|
|
65
|
+
- Honest token-shape documentation (`pvt_` is modern; `pvk_` is legacy and still accepted).
|
|
66
|
+
- README tells the truth about what `vault init` writes to `~/.claude.json` — a vault-scoped URL with a baked-in `pvt_` bearer, not OAuth.
|
|
67
|
+
|
|
68
|
+
### Removed
|
|
69
|
+
|
|
70
|
+
- **Semantic / vector search** — the embeddings path (`sqlite-vec`, `semantic-search` tool, embedding-provider setup wizard, `/api/ingest` endpoint). Full-text search via `query-notes` `search=` remains.
|
|
71
|
+
- **`parachute vault keys` subcommand** — superseded by `parachute vault tokens`. Legacy `pvk_...` keys in `config.yaml` are still honored at runtime.
|
|
72
|
+
|
|
73
|
+
### For contributors
|
|
74
|
+
|
|
75
|
+
- **Async `Store` interface**, renamed to `BunSqliteStore`. Paves the way for Durable Object SQLite and R2 blob backends (in flight).
|
|
76
|
+
- **`src/routing.ts`** extracted from `src/server.ts` so the request dispatcher is unit-testable without spinning up `Bun.serve()`.
|
|
77
|
+
- **`core/src/test-preload.ts`** isolates `PARACHUTE_HOME` for tests so `bun test` never touches a user's real `~/.parachute/`.
|
|
78
|
+
- Test suite at release cut: **538 passing / 0 failing / 3 skipped** across 22 files (541 tests total).
|
|
79
|
+
|
|
80
|
+
[0.2.0]: https://github.com/ParachuteComputer/parachute-vault/releases/tag/v0.2.0
|
package/CLAUDE.md
CHANGED
|
@@ -6,9 +6,9 @@ Agent-native knowledge graph. Notes, tags, links over MCP. Self-hosted, one comm
|
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
parachute vault init → ~/.parachute/ (config, .env, daemon, MCP)
|
|
9
|
-
parachute vault create → new vault (SQLite DB + vault.yaml +
|
|
9
|
+
parachute vault create → new vault (SQLite DB + vault.yaml + pvt_ token)
|
|
10
10
|
parachute vault config → manage env vars (PORT, etc.)
|
|
11
|
-
parachute vault
|
|
11
|
+
parachute vault tokens → list / create / revoke per-vault tokens
|
|
12
12
|
|
|
13
13
|
CLI → Bun server (port 1940) → multiple vaults (each its own SQLite DB)
|
|
14
14
|
↑
|
package/README.md
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# Parachute Vault
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Parachute Vault is a self-hosted knowledge graph that any AI can read and write, over the open [MCP](https://modelcontextprotocol.io) protocol.** Your notes, tags, links, and attachments live on your machine — in plain SQLite databases under `~/.parachute/`, not in a vendor's cloud.
|
|
4
|
+
|
|
5
|
+
Works with Claude, ChatGPT, Gemini, or any future MCP-capable AI. Switch tools without losing your knowledge. No vendor lock-in, no re-import step when the next model lands. One command to install; one OAuth consent to connect each AI client.
|
|
4
6
|
|
|
5
7
|
## Quick start
|
|
6
8
|
|
|
7
|
-
Requires [Bun](https://bun.sh) (`curl -fsSL https://bun.sh/install | bash`).
|
|
9
|
+
Requires [Bun](https://bun.sh) (`curl -fsSL https://bun.sh/install | bash`) and macOS 13+ or Linux. No root needed — see [Requirements](#requirements) below for specifics.
|
|
8
10
|
|
|
9
11
|
```bash
|
|
10
12
|
# Install globally (registers the `parachute` CLI)
|
|
11
|
-
bun add -g
|
|
13
|
+
bun add -g @openparachute/vault
|
|
12
14
|
parachute vault init
|
|
13
15
|
|
|
14
16
|
# Or clone and run directly
|
|
@@ -35,17 +37,167 @@ A server on port 1940 with:
|
|
|
35
37
|
|
|
36
38
|
Each vault is its own SQLite database. Run multiple vaults on one server.
|
|
37
39
|
|
|
40
|
+
## What `vault init` does
|
|
41
|
+
|
|
42
|
+
A mental model for "where is my data?" and "what can I poke at?" after the one-command setup.
|
|
43
|
+
|
|
44
|
+
### On disk — `~/.parachute/`
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
~/.parachute/
|
|
48
|
+
config.yaml # global config — port, default_vault, owner password hash,
|
|
49
|
+
# TOTP secret, backup-codes hashes, backup schedule. 0600.
|
|
50
|
+
.env # runtime env vars (PORT=1940 by default; any webhook API
|
|
51
|
+
# keys you add later). Sourced by the daemon wrapper.
|
|
52
|
+
vault.log # stdout of the running daemon (tail via `parachute vault logs`)
|
|
53
|
+
vault.err # stderr of the running daemon
|
|
54
|
+
server-path # text file: absolute path to the repo's src/server.ts —
|
|
55
|
+
# how the daemon wrapper finds the source after you move it
|
|
56
|
+
start.sh # the wrapper launchd/systemd execs. Knows the absolute
|
|
57
|
+
# path to `bun` so a later PATH change doesn't break the daemon
|
|
58
|
+
assets/ # legacy top-level uploads dir (attachments now land per-vault)
|
|
59
|
+
vaults/ # one subdirectory per vault
|
|
60
|
+
default/
|
|
61
|
+
vault.db # the SQLite database — notes, tags, links, attachments,
|
|
62
|
+
# per-vault tokens, OAuth clients + codes, tag schemas
|
|
63
|
+
vault.yaml # per-vault config — description (sent as MCP session
|
|
64
|
+
# instruction), published_tag override, legacy api_keys
|
|
65
|
+
assets/ # per-vault uploaded attachments (audio, images)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`config.yaml` is the one file written at 0600 because it holds the bcrypt owner-password hash and the plaintext TOTP secret. `.env` is written with your umask default (typically 0644); if you add webhook API keys there, tighten the mode yourself. SQLite DBs follow your umask.
|
|
69
|
+
|
|
70
|
+
### Registered externally
|
|
71
|
+
|
|
72
|
+
- **macOS**: a launchd user agent labelled `computer.parachute.vault` (plus `computer.parachute.vault.backup` if you ran `vault backup --schedule`).
|
|
73
|
+
- **Linux + systemd**: a user service named `parachute-vault.service` (managed via `systemctl --user`).
|
|
74
|
+
- **Neither of the above**: `vault init` prints a reminder to start the server yourself (`bun src/server.ts` or Docker). No service registration.
|
|
75
|
+
|
|
76
|
+
The daemon binds `0.0.0.0:1940` (or whatever you set in `PORT`) and serves REST, MCP, and OAuth routes. `parachute vault status` is the fast check; `parachute vault url` prints just the URL for use in scripts.
|
|
77
|
+
|
|
78
|
+
### `~/.claude.json`
|
|
79
|
+
|
|
80
|
+
`vault init` adds one entry — `mcpServers["parachute-vault"]` — pointing at `http://127.0.0.1:<port>/vaults/<default-vault>/mcp` with a baked-in `Authorization: Bearer pvt_...` header. Next Claude Code session picks it up; there's no further wiring. See [Connecting a client](#connecting-a-client) for rotating that token or pointing it elsewhere.
|
|
81
|
+
|
|
82
|
+
### Your API token
|
|
83
|
+
|
|
84
|
+
The `pvt_...` token printed at init is the one baked into `~/.claude.json`. It's not stored anywhere retrievable — save it if you need it for `curl`, cron, or any other script. Lost it? Just mint a new one: `parachute vault tokens create`. Tokens are SHA-256 hashed at rest in each vault's `vault.db`.
|
|
85
|
+
|
|
86
|
+
### Owner password prompt
|
|
87
|
+
|
|
88
|
+
Init pauses for one interactive prompt: "Set an owner password for OAuth consent?" The password is what the consent page asks for when Claude Desktop / Parachute Daily / any browser-OAuth client connects. You can skip it and set it later with `parachute vault set-password`; without it, the consent page falls back to pasting a vault token. See [Connecting a client → Owner password](#owner-password-needed-for-oauth).
|
|
89
|
+
|
|
90
|
+
## Connecting a client
|
|
91
|
+
|
|
92
|
+
Two ways to authenticate — pick based on the client, not the deployment:
|
|
93
|
+
|
|
94
|
+
| Path | When to use | User action |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| **OAuth 2.1 + PKCE (browser flow)** | Claude Desktop, Parachute Daily, any third-party MCP client set up interactively | Click "Add integration", enter server URL, a browser opens to the vault's consent page, you enter the owner password, done — no token ever touches your clipboard |
|
|
97
|
+
| **Bearer token** | Claude Code (auto-wired by `vault init`), CLI scripts, cron jobs, any non-interactive caller | `curl -H "Authorization: Bearer pvt_..."` — the token is printed once at `vault init` (save it) or minted on demand with `parachute vault tokens create` |
|
|
98
|
+
|
|
99
|
+
Both paths end up with the same kind of token in the vault's DB — a `pvt_` string, scoped to one vault and one permission level (`full` or `read`). OAuth just moves the "how does the client get that token" step from "human copy-pastes it" to "browser-based handshake with the owner's consent."
|
|
100
|
+
|
|
101
|
+
### Owner password (needed for OAuth)
|
|
102
|
+
|
|
103
|
+
`vault init` prompts you to set an owner password (minimum 12 characters). This is what the OAuth consent page asks for when a client requests access. If you skip the prompt, OAuth still works but the consent page falls back to asking for a vault token instead — functional but clunky. Set it later with:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
parachute vault set-password # set / change
|
|
107
|
+
parachute vault set-password --clear # remove (reverts to token fallback)
|
|
108
|
+
parachute vault 2fa enroll # optional: add TOTP 2FA on top
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Password and 2FA secrets live in `~/.parachute/config.yaml` at mode 0600 (bcrypt hash + base32 TOTP secret).
|
|
112
|
+
|
|
113
|
+
### Claude Code
|
|
114
|
+
|
|
115
|
+
`vault init` fully auto-configures `~/.claude.json` — there's nothing else to do. The entry it writes uses a baked-in `pvt_` token rather than OAuth:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"mcpServers": {
|
|
120
|
+
"parachute-vault": {
|
|
121
|
+
"type": "http",
|
|
122
|
+
"url": "http://127.0.0.1:1940/vaults/{name}/mcp",
|
|
123
|
+
"headers": { "Authorization": "Bearer pvt_..." }
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Where `{name}` is `default` on a fresh install, or whatever vault you pointed `vault init` at. **First MCP call after `vault init` requires no browser handoff — Claude Code uses the baked-in token and the vault's tools show up in your next session.** This is intentional: for an owner connecting their own machine's vault to their own Claude Code, the token is already there and OAuth would add friction.
|
|
130
|
+
|
|
131
|
+
To re-point Claude Code at a different vault, change `default_vault` in `~/.parachute/config.yaml` and re-run `parachute vault init` — which re-mints an API token and re-writes the `~/.claude.json` entry end-to-end. To rotate the token only, edit `~/.claude.json` and replace the `Authorization` header value with a fresh token from `parachute vault tokens create`. (Running `parachute vault mcp-install` on its own overwrites the MCP entry *without* an `Authorization` header and is intended for the rare case where you want to drop the token and connect via OAuth instead.)
|
|
132
|
+
|
|
133
|
+
### Claude Desktop (OAuth)
|
|
134
|
+
|
|
135
|
+
For Claude Desktop — or any install where the server is on a different machine from the client — use the browser-based OAuth flow:
|
|
136
|
+
|
|
137
|
+
1. Claude Desktop → Settings → Integrations → Add MCP server.
|
|
138
|
+
2. Enter the URL: `https://vault.yourdomain.com/vaults/{name}/mcp` (replace `{name}`, or use the unscoped `https://vault.yourdomain.com/mcp` on a single-vault deployment). **Do not paste a bearer token** — leave the auth field empty.
|
|
139
|
+
3. An OAuth-capable MCP client discovers the vault's authorization server at `/.well-known/oauth-authorization-server`, registers itself via Dynamic Client Registration (RFC 7591), and opens your browser to the vault's consent page.
|
|
140
|
+
4. Enter your owner password (plus TOTP code / backup code if 2FA is enabled), pick a scope (`full` or `read`), click Authorize.
|
|
141
|
+
5. Browser redirects back. The connection is live. The client now holds a `pvt_` token scoped to this vault.
|
|
142
|
+
|
|
143
|
+
If you'd rather skip OAuth — e.g. you're scripting the setup — Claude Desktop also accepts a bearer token via the integration's auth header field. Use a token from `parachute vault tokens create` (or the one from `vault init` if you still have it). This is the "manual bearer" fallback; OAuth is the recommended path.
|
|
144
|
+
|
|
145
|
+
### Parachute Daily (mobile)
|
|
146
|
+
|
|
147
|
+
Daily uses the same OAuth flow. On first launch: enter the server URL, pick the vault from the drop-down (populated from the public `GET /vaults/list` endpoint), tap **Connect to Vault**. The same consent-page handoff runs in your phone's browser, then redirects back to the app via the `parachute://oauth/callback` deep link. The app stores the `pvt_` token in platform secure storage.
|
|
148
|
+
|
|
149
|
+
### Multi-vault
|
|
150
|
+
|
|
151
|
+
One server, many vaults. Each vault is its own SQLite DB with its own MCP endpoint, its own OAuth, and its own tokens.
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
parachute vault create work # new vault named "work"
|
|
155
|
+
parachute vault list # show all vaults on this server
|
|
156
|
+
parachute vault remove work --yes
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**The default vault is managed for you.** `vault init` creates `default` on first install and records it as `default_vault` in `~/.parachute/config.yaml`. `vault create <name>` promotes the newly-created vault to default when no default exists or when the configured default points at a missing vault. `vault remove <name>` promotes the sole survivor when you delete the default and one vault remains; if multiple remain after removing the default, it clears the setting and tells you to edit `config.yaml` yourself. There is no `vault set-default` subcommand — to point the server at a different existing vault, edit the `default_vault:` line in `~/.parachute/config.yaml` and `parachute vault restart`.
|
|
160
|
+
|
|
161
|
+
**Single-vault rule.** When the server has exactly one vault, the unscoped `/oauth/*` and `/mcp` paths transparently resolve to it — regardless of its name. A lone vault named `journal` works at `https://vault.example.com/mcp` with no vault-in-URL needed.
|
|
162
|
+
|
|
163
|
+
**Multi-vault rule.** When the server has two or more vaults, always use the vault-scoped path (`/vaults/{name}/mcp`, `/vaults/{name}/oauth/authorize`). OAuth tokens minted there are scoped to that vault alone — cross-vault substitution is enforced at the OAuth layer: an auth code minted for one vault cannot be redeemed at another vault's token endpoint.
|
|
164
|
+
|
|
165
|
+
**Listing vaults from a client.** The authenticated `GET /vaults` endpoint returns full vault metadata. The public `GET /vaults/list` endpoint returns names only, no metadata, no auth required — this is what Parachute Daily's vault picker calls before the user authenticates. Operators who want to hide the vault list from unauthenticated callers can set `discovery: disabled` in `~/.parachute/config.yaml` to make `/vaults/list` return 404.
|
|
166
|
+
|
|
38
167
|
## CLI
|
|
39
168
|
|
|
40
169
|
```bash
|
|
41
170
|
# Setup
|
|
42
|
-
parachute vault init # one-command setup
|
|
171
|
+
parachute vault init # one-command setup (idempotent — safe to re-run)
|
|
43
172
|
parachute vault status # check what's running
|
|
173
|
+
parachute vault doctor # diagnose install/config issues (see Troubleshooting)
|
|
174
|
+
parachute vault url # print the local server URL (for scripts)
|
|
175
|
+
parachute vault uninstall # remove daemon + MCP entry; keeps user data
|
|
176
|
+
parachute vault uninstall --wipe # ...and also remove vaults, .env, config.yaml, logs
|
|
177
|
+
parachute vault uninstall --yes --wipe # scripted destructive wipe (prints an audit line)
|
|
44
178
|
|
|
45
179
|
# Vaults
|
|
46
180
|
parachute vault create work # create a new vault
|
|
47
|
-
parachute vault list # list all vaults
|
|
48
|
-
parachute vault remove work --yes # delete a vault
|
|
181
|
+
parachute vault list # list all vaults (alias: `ls`)
|
|
182
|
+
parachute vault remove work --yes # delete a vault (alias: `rm`)
|
|
183
|
+
parachute vault mcp-install # (re)write the ~/.claude.json MCP entry for the default vault
|
|
184
|
+
|
|
185
|
+
# OAuth — owner password + 2FA
|
|
186
|
+
parachute vault set-password # set/change the owner password (OAuth consent page)
|
|
187
|
+
parachute vault set-password --clear # remove the owner password (falls back to vault-token auth)
|
|
188
|
+
parachute vault 2fa status # show 2FA state + remaining backup codes
|
|
189
|
+
parachute vault 2fa enroll # enroll TOTP (shows QR + prints one-time backup codes)
|
|
190
|
+
parachute vault 2fa disable # disable 2FA (requires password or TOTP/backup code)
|
|
191
|
+
parachute vault 2fa backup-codes # regenerate backup codes (invalidates the old set)
|
|
192
|
+
|
|
193
|
+
# Tokens
|
|
194
|
+
parachute vault tokens # list all tokens across all vaults
|
|
195
|
+
parachute vault tokens create # full-access token in the default vault
|
|
196
|
+
parachute vault tokens create --vault work # ...in a specific vault
|
|
197
|
+
parachute vault tokens create --read # read-only token
|
|
198
|
+
parachute vault tokens create --expires 30d # expiring token (N{h|d|w|m|y})
|
|
199
|
+
parachute vault tokens create --label mobile # labeled token
|
|
200
|
+
parachute vault tokens revoke <token-id> # revoke (default vault; add --vault to target)
|
|
49
201
|
|
|
50
202
|
# Obsidian
|
|
51
203
|
parachute vault import ~/Obsidian/MyVault # import into default vault
|
|
@@ -53,18 +205,20 @@ parachute vault import ~/Obsidian/Work --vault work # import into a specific
|
|
|
53
205
|
parachute vault import <path> --dry-run # preview without importing
|
|
54
206
|
parachute vault export ./output --vault work # export a specific vault
|
|
55
207
|
|
|
56
|
-
# Tokens
|
|
57
|
-
parachute vault tokens # list all tokens
|
|
58
|
-
parachute vault tokens create --vault work # new full-access token
|
|
59
|
-
parachute vault tokens create --vault work --read # read-only token
|
|
60
|
-
parachute vault tokens create --vault work --expires 30d # token with expiry
|
|
61
|
-
parachute vault tokens create --vault work --label mobile # labeled token
|
|
62
|
-
parachute vault tokens revoke <token-id> --vault work # revoke a token
|
|
63
|
-
|
|
64
208
|
# Config
|
|
65
|
-
parachute vault config # show
|
|
66
|
-
parachute vault config set KEY value # set
|
|
67
|
-
parachute vault
|
|
209
|
+
parachute vault config # show current configuration
|
|
210
|
+
parachute vault config set KEY value # set an env var (e.g. PORT=1940)
|
|
211
|
+
parachute vault config unset KEY # remove an env var
|
|
212
|
+
parachute vault restart # apply config changes (bounces the daemon)
|
|
213
|
+
|
|
214
|
+
# Server
|
|
215
|
+
parachute vault serve # run the server in the foreground (no daemon)
|
|
216
|
+
parachute vault logs # stream vault.log + vault.err (tail -f)
|
|
217
|
+
|
|
218
|
+
# Backup
|
|
219
|
+
parachute vault backup # one-shot backup to configured destinations
|
|
220
|
+
parachute vault backup --schedule daily # hourly | daily | weekly | manual (macOS launchd)
|
|
221
|
+
parachute vault backup status # schedule, last run, destinations, next run
|
|
68
222
|
```
|
|
69
223
|
|
|
70
224
|
## MCP tools (9)
|
|
@@ -164,12 +318,57 @@ triggers:
|
|
|
164
318
|
|
|
165
319
|
Webhook servers (scribe, narrate) are stateless — they don't need vault's API key.
|
|
166
320
|
|
|
321
|
+
### Backing up your vault
|
|
322
|
+
|
|
323
|
+
Your vault is just SQLite DBs + a handful of YAML files under `~/.parachute/`. `parachute vault backup` snapshots everything into a single timestamped tarball, for a one-shot or a scheduled run.
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
parachute vault backup # one-shot — snapshot + ship to destinations
|
|
327
|
+
parachute vault backup --schedule daily # register a launchd agent (macOS)
|
|
328
|
+
parachute vault backup --schedule manual # stop scheduled backups
|
|
329
|
+
parachute vault backup status # schedule, last run, destinations, next run
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Configure destinations in `~/.parachute/config.yaml`:
|
|
333
|
+
|
|
334
|
+
```yaml
|
|
335
|
+
backup:
|
|
336
|
+
schedule: daily # hourly | daily | weekly | manual
|
|
337
|
+
retention:
|
|
338
|
+
daily: 7 # last 7 daily snapshots
|
|
339
|
+
weekly: 4 # last-of-week for 4 weeks
|
|
340
|
+
monthly: 12 # last-of-month for 12 months
|
|
341
|
+
yearly: null # last-of-year, unbounded (null = keep every year forever)
|
|
342
|
+
destinations:
|
|
343
|
+
- kind: local
|
|
344
|
+
path: ~/Library/Mobile Documents/com~apple~CloudDocs/parachute-backups
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**Retention is tiered** (grandfather / father / son). After each run, the pruner keeps the union of four tiers:
|
|
348
|
+
|
|
349
|
+
| Tier | What it keeps |
|
|
350
|
+
|---------|--------------------------------------------------------|
|
|
351
|
+
| daily | The N most recent snapshots. |
|
|
352
|
+
| weekly | The last snapshot of each of the last N ISO weeks. |
|
|
353
|
+
| monthly | The last snapshot of each of the last N calendar months.|
|
|
354
|
+
| yearly | The last snapshot of each year — `null` means unbounded.|
|
|
355
|
+
|
|
356
|
+
A snapshot that qualifies for multiple tiers is kept once. Set any tier to `0` to disable it; sparse data (days without a backup) just means some tiers contribute nothing that day. Bucketing uses your local timezone, so calendars line up with what you see, not UTC.
|
|
357
|
+
|
|
358
|
+
**What's in a snapshot**: atomic `VACUUM INTO` copies of every `vaults/<name>/vault.db`, your `config.yaml`, and each vault's `vault.yaml`, bundled as `parachute-backup-<timestamp>.tar.gz`. Safe under concurrent reads/writes — no need to stop the daemon.
|
|
359
|
+
|
|
360
|
+
**Restore**: extract the tarball into a fresh `~/.parachute/` and run `parachute vault init` to re-register the daemon. The DBs and configs drop in place; you don't need any special restore command (for now — a dedicated `vault restore` is coming soon).
|
|
361
|
+
|
|
362
|
+
Destination kinds shipping in this release: `local` (any filesystem path — including iCloud Drive, a mounted external disk, or an rsync/Syncthing-backed folder). `s3`, `rsync`, and `cloud` destinations are planned but not yet implemented.
|
|
363
|
+
|
|
364
|
+
On Linux, scheduled runs via systemd timers are a follow-up; for now `parachute vault backup` works on Linux but you'll need to wire the cron yourself.
|
|
365
|
+
|
|
167
366
|
### View endpoint
|
|
168
367
|
|
|
169
368
|
Serve notes as clean HTML pages at `/view/:noteId`:
|
|
170
369
|
|
|
171
370
|
- **Without auth**: only serves notes tagged `published` (or with `metadata.published: true`). Returns 404 for unpublished notes.
|
|
172
|
-
- **With auth**: serves any note. Pass
|
|
371
|
+
- **With auth**: serves any note. Pass your token via `Authorization: Bearer pvt_...` header or `?key=pvt_...` query param.
|
|
173
372
|
- **Custom tag**: set `published_tag` in vault.yaml to use a different tag name (default: `publish`).
|
|
174
373
|
|
|
175
374
|
```yaml
|
|
@@ -212,11 +411,14 @@ Metadata is a JSON column. Vaults start blank — no predefined tags or schema.
|
|
|
212
411
|
|
|
213
412
|
**All API and MCP requests require a valid API key.** No exceptions — localhost gets no special treatment.
|
|
214
413
|
|
|
215
|
-
|
|
414
|
+
For wiring up an AI client (Claude Code, Claude Desktop, Parachute Daily), see [Connecting a client](#connecting-a-client) above. This section covers token-level details: how to pass a key, how to manage tokens, and which endpoints are public by design (`/health`, published notes at `/view/:id`).
|
|
216
415
|
|
|
217
416
|
### Passing the key
|
|
218
417
|
|
|
219
|
-
|
|
418
|
+
Tokens come in two shapes. Both work interchangeably at every authenticated endpoint:
|
|
419
|
+
|
|
420
|
+
- `pvt_...` — per-vault scoped tokens (the modern format; what `vault init` mints, what OAuth issues, what `parachute vault tokens create` produces)
|
|
421
|
+
- `pvk_...` — legacy global API keys from `config.yaml` (still honored for existing deployments)
|
|
220
422
|
|
|
221
423
|
```bash
|
|
222
424
|
# Header (preferred)
|
|
@@ -229,26 +431,6 @@ curl -H "X-API-Key: pvt_..." http://localhost:1940/api/notes
|
|
|
229
431
|
curl http://localhost:1940/view/noteId?key=pvt_...
|
|
230
432
|
```
|
|
231
433
|
|
|
232
|
-
### Claude Desktop
|
|
233
|
-
|
|
234
|
-
Settings → Integrations → Add MCP → URL: `https://vault.yourdomain.com/mcp`, Header: `Authorization: Bearer pvk_...`
|
|
235
|
-
|
|
236
|
-
### Claude Code
|
|
237
|
-
|
|
238
|
-
`vault init` auto-configures `~/.claude.json`. To set manually:
|
|
239
|
-
|
|
240
|
-
```json
|
|
241
|
-
{
|
|
242
|
-
"mcpServers": {
|
|
243
|
-
"parachute-vault": {
|
|
244
|
-
"type": "http",
|
|
245
|
-
"url": "http://127.0.0.1:1940/mcp",
|
|
246
|
-
"headers": { "Authorization": "Bearer pvk_..." }
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
```
|
|
251
|
-
|
|
252
434
|
### Token management
|
|
253
435
|
|
|
254
436
|
Per-vault tokens with two permission levels:
|
|
@@ -294,6 +476,37 @@ For remote access, always use a TLS-terminating proxy:
|
|
|
294
476
|
| Direct LAN IP (no TLS) | Plaintext on WiFi | Avoid |
|
|
295
477
|
| Direct internet (no TLS) | Plaintext on internet | Never do this |
|
|
296
478
|
|
|
479
|
+
## Troubleshooting
|
|
480
|
+
|
|
481
|
+
### `parachute vault doctor` is your first stop
|
|
482
|
+
|
|
483
|
+
`doctor` inspects the install and prints one line per check with a status (`✓` pass, `!` warn, `✗` fail) and, when relevant, a suggested fix. It exits 1 on any `fail` and 0 otherwise. Run it any time something feels off.
|
|
484
|
+
|
|
485
|
+
The checks, in the order they're emitted:
|
|
486
|
+
|
|
487
|
+
| Check | What it verifies | Typical fix when failing |
|
|
488
|
+
|---|---|---|
|
|
489
|
+
| server-path pointer | `~/.parachute/server-path` exists, is non-empty, and points at a `src/server.ts` that actually exists. This is where the stale-path failure after a repo move shows up first. | `parachute vault init` from the current repo location. |
|
|
490
|
+
| wrapper script | `~/.parachute/start.sh` exists. Without it, launchd / systemd has nothing to exec. | `parachute vault init`. |
|
|
491
|
+
| launchd agent (macOS) / systemd service (Linux) | The daemon is registered and loaded/active. On Linux without systemd, the check is silently skipped. | `parachute vault restart` or re-run `vault init`. |
|
|
492
|
+
| bun on PATH | `bun` is resolvable via your shell's PATH. Not required once the daemon is installed (`start.sh` embeds an absolute bun path at init time) but missing bun is the #1 first-time-user failure. | `curl -fsSL https://bun.sh/install \| bash` and restart the shell. |
|
|
493
|
+
| MCP entry in `~/.claude.json` | An entry is present. When it is, two follow-ups: the URL's port matches the running vault's port, and the MCP URL is reachable over HTTP (any response — even 401 — counts as reachable). | `parachute vault mcp-install` to rewrite the entry, or `parachute vault restart` if the daemon is down. |
|
|
494
|
+
| port `1940` availability | Probes via `lsof` / `ss` and classifies: free, held by our daemon (pass), held by a foreign process (warn), or unknown (tool unavailable → check silently omitted). | Stop the conflicting process, or set a different `PORT` in `~/.parachute/.env` and re-run `vault init`. |
|
|
495
|
+
| backup agent (macOS, only when `backup.schedule != manual`) | The scheduled-backup launchd agent is loaded. | `parachute vault backup --schedule <hourly\|daily\|weekly>` to reinstall the agent. |
|
|
496
|
+
| backup destinations (only when `backup.schedule != manual`) | At least one destination is configured; each configured destination is writable. | Edit `~/.parachute/config.yaml` under `backup.destinations`, or fix the path's permissions. |
|
|
497
|
+
|
|
498
|
+
### Common failure modes
|
|
499
|
+
|
|
500
|
+
- **Daemon won't start after a port change.** `~/.parachute/.env` has the new `PORT=...` but the daemon is still trying to bind the old one, or something else already holds the new port. `parachute vault doctor` surfaces both conditions. Fix the holder (or pick a different port) and `parachute vault restart`.
|
|
501
|
+
- **MCP entry is stale after moving the repo.** launchd/systemd keeps pointing at the old path. `doctor` flags this as a failed `server.ts at pointer target` check; `parachute vault init` from the new location rewrites the pointer, wrapper, and daemon registration.
|
|
502
|
+
- **Claude Code shows no vault tools.** Check in order: (1) is the daemon up (`parachute vault status`)? (2) does `~/.claude.json` have a `parachute-vault` entry with both `url` and a valid `Authorization` header? (3) does the URL's vault name match an existing vault? `parachute vault doctor` catches the first two. A missing or stale `Authorization` header after a bare `vault mcp-install` is the usual culprit for #2 — see the Claude Code section of [Connecting a client](#connecting-a-client) for how to rewrite it.
|
|
503
|
+
- **Claude Desktop / Daily won't connect via OAuth.** If the owner-password prompt was skipped at `vault init`, the consent page falls back to requiring a vault token in place of the password (functional but clunky). Set one now with `parachute vault set-password`. If 2FA is enrolled, have your authenticator app ready before starting the flow; lost TOTP access recovers via the backup codes printed at enrollment.
|
|
504
|
+
- **Scheduled backups aren't running.** On macOS: `doctor` flags `backup agent: not loaded` when `schedule` isn't `manual` but the launchd agent is missing — rerun `parachute vault backup --schedule <freq>` to reinstall it. On Linux: systemd-timer support for backup isn't shipped yet, so `--schedule daily` silently skips the scheduler. Run `parachute vault backup` from cron (or similar) until that lands.
|
|
505
|
+
|
|
506
|
+
### Getting help
|
|
507
|
+
|
|
508
|
+
If `doctor` is all-green but something still isn't working, capture the output alongside `parachute vault status` and open an issue at <https://github.com/ParachuteComputer/parachute-vault/issues>. Redact tokens from any logs before attaching.
|
|
509
|
+
|
|
297
510
|
## Deployment
|
|
298
511
|
|
|
299
512
|
### Remote access via Cloudflare Tunnel (free)
|
|
@@ -331,7 +544,37 @@ sudo cloudflared service install
|
|
|
331
544
|
sudo systemctl start cloudflared
|
|
332
545
|
```
|
|
333
546
|
|
|
334
|
-
Then
|
|
547
|
+
Then point any client at `https://vault.yourdomain.com/vaults/{name}/mcp` (or `https://vault.yourdomain.com/mcp` for a single-vault deployment). See [Connecting a client → Claude Desktop (OAuth)](#claude-desktop-oauth) — the flow is identical to the local case once the URL is remote; the browser-based OAuth handshake makes the connection without pasting a bearer token.
|
|
548
|
+
|
|
549
|
+
### Remote access via Tailscale Funnel
|
|
550
|
+
|
|
551
|
+
If you're already on [Tailscale](https://tailscale.com), Funnel is the shortest path to a public HTTPS URL — no custom domain, no reverse-proxy config, no cert management. Good for a single-user vault or a vault shared with a handful of people; the edge has bandwidth and connection-count caps, so not suited to heavy traffic.
|
|
552
|
+
|
|
553
|
+
Prerequisites: Tailscale v1.52 or later (earlier versions use a two-command `tailscale serve` + `tailscale funnel on` form that is now deprecated), the tailnet must have MagicDNS and HTTPS enabled in the admin console, and the `funnel` node attribute must be granted in your ACLs. The CLI adds the ACL entry on first use if you're the tailnet owner.
|
|
554
|
+
|
|
555
|
+
Expose the vault:
|
|
556
|
+
|
|
557
|
+
```bash
|
|
558
|
+
# One command — Tailscale provisions the HTTPS cert, updates the ACL if needed,
|
|
559
|
+
# and registers a persistent funnel that survives reboots.
|
|
560
|
+
tailscale funnel --bg --https=443 localhost:1940
|
|
561
|
+
|
|
562
|
+
# See what's being served and on which tailnet hostname
|
|
563
|
+
tailscale funnel status
|
|
564
|
+
|
|
565
|
+
# Take it down later
|
|
566
|
+
tailscale funnel --https=443 localhost:1940 off
|
|
567
|
+
# ...or nuke the whole funnel config
|
|
568
|
+
tailscale funnel reset
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
The resulting URL is `https://<your-device>.<your-tailnet>.ts.net/` — `tailscale funnel status` prints it verbatim. You can also use ports `8443` or `10000` via `--https=<port>`; no other public ports are available to Funnel.
|
|
572
|
+
|
|
573
|
+
Point any MCP client at the Tailscale URL:
|
|
574
|
+
- Claude Desktop → Settings → Integrations → Add MCP → `https://<your-device>.<your-tailnet>.ts.net/vaults/{name}/mcp` (leave the Authorization field empty; the OAuth flow will handle it — see [Connecting a client](#connecting-a-client)).
|
|
575
|
+
- Parachute Daily → enter the base URL `https://<your-device>.<your-tailnet>.ts.net`, pick the vault, tap Connect.
|
|
576
|
+
|
|
577
|
+
**Cloudflare vs Tailscale, at a glance.** Pick Cloudflare when you want a custom domain, bandwidth headroom for heavier traffic, or to share the vault with people who aren't on your tailnet. Pick Tailscale when you're already running it, you're fine with a `*.ts.net` URL, and you want the setup to fit in two commands.
|
|
335
578
|
|
|
336
579
|
### Docker
|
|
337
580
|
|
|
@@ -347,9 +590,11 @@ docker compose up -d
|
|
|
347
590
|
|
|
348
591
|
## Requirements
|
|
349
592
|
|
|
350
|
-
-
|
|
351
|
-
- macOS (launchd) or Linux (systemd
|
|
352
|
-
-
|
|
593
|
+
- **Bun** — `curl -fsSL https://bun.sh/install | bash` ([bun.sh](https://bun.sh)).
|
|
594
|
+
- **macOS 13+** (launchd user agent) **or Linux** (systemd user service; other init systems work if you start the server yourself).
|
|
595
|
+
- **No root / sudo required** — `vault init` writes to your user home (`~/.parachute/`) and registers the daemon in your user scope only. Never touches system paths or global services.
|
|
596
|
+
- **Not supported on Windows natively.** WSL2 hasn't been tested; file an issue if you try it and want it to work.
|
|
597
|
+
- Optional, for remote access: [cloudflared](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/) (Cloudflare Tunnel) or [Tailscale](https://tailscale.com) — see [Deployment](#deployment).
|
|
353
598
|
|
|
354
599
|
## License
|
|
355
600
|
|