sealkeep 0.5.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.
Files changed (180) hide show
  1. package/ARCHITECTURE.md +201 -0
  2. package/CHANGELOG.md +218 -0
  3. package/CONTROL_PLANE.md +86 -0
  4. package/LICENSE +34 -0
  5. package/README.md +249 -0
  6. package/THIRD_PARTY.md +22 -0
  7. package/THREAT_MODEL.md +107 -0
  8. package/dist/packages/vaultline-crypto/src/aead.d.ts +12 -0
  9. package/dist/packages/vaultline-crypto/src/aead.js +24 -0
  10. package/dist/packages/vaultline-crypto/src/chunk-access.d.ts +39 -0
  11. package/dist/packages/vaultline-crypto/src/chunk-access.js +93 -0
  12. package/dist/packages/vaultline-crypto/src/envelope.d.ts +71 -0
  13. package/dist/packages/vaultline-crypto/src/envelope.js +188 -0
  14. package/dist/packages/vaultline-crypto/src/format.d.ts +106 -0
  15. package/dist/packages/vaultline-crypto/src/format.js +43 -0
  16. package/dist/packages/vaultline-crypto/src/index.d.ts +5 -0
  17. package/dist/packages/vaultline-crypto/src/index.js +5 -0
  18. package/dist/packages/vaultline-crypto/src/recipients.d.ts +42 -0
  19. package/dist/packages/vaultline-crypto/src/recipients.js +129 -0
  20. package/dist/packages/vaultline-crypto/src/sha256-stream.d.ts +41 -0
  21. package/dist/packages/vaultline-crypto/src/sha256-stream.js +206 -0
  22. package/dist/packages/vaultline-crypto/src/stream.d.ts +139 -0
  23. package/dist/packages/vaultline-crypto/src/stream.js +477 -0
  24. package/dist/site/index.html +1542 -0
  25. package/dist/site.zip +0 -0
  26. package/dist/src/activity.d.ts +22 -0
  27. package/dist/src/activity.js +52 -0
  28. package/dist/src/adapters.d.ts +212 -0
  29. package/dist/src/adapters.js +533 -0
  30. package/dist/src/audit.d.ts +24 -0
  31. package/dist/src/audit.js +41 -0
  32. package/dist/src/autopilot.d.ts +77 -0
  33. package/dist/src/autopilot.js +148 -0
  34. package/dist/src/bip39-wordlist.d.ts +15 -0
  35. package/dist/src/bip39-wordlist.js +272 -0
  36. package/dist/src/branding.d.ts +31 -0
  37. package/dist/src/branding.js +31 -0
  38. package/dist/src/chunk-store.d.ts +142 -0
  39. package/dist/src/chunk-store.js +502 -0
  40. package/dist/src/cli.d.ts +2 -0
  41. package/dist/src/cli.js +2035 -0
  42. package/dist/src/cloud.d.ts +434 -0
  43. package/dist/src/cloud.js +851 -0
  44. package/dist/src/control-plane/auth.d.ts +62 -0
  45. package/dist/src/control-plane/auth.js +123 -0
  46. package/dist/src/control-plane/server.d.ts +31 -0
  47. package/dist/src/control-plane/server.js +263 -0
  48. package/dist/src/control-plane/store.d.ts +101 -0
  49. package/dist/src/control-plane/store.js +82 -0
  50. package/dist/src/control-plane-cli.d.ts +2 -0
  51. package/dist/src/control-plane-cli.js +37 -0
  52. package/dist/src/control-plane-server.d.ts +10 -0
  53. package/dist/src/control-plane-server.js +11 -0
  54. package/dist/src/control-plane.d.ts +78 -0
  55. package/dist/src/control-plane.js +61 -0
  56. package/dist/src/crypto.d.ts +56 -0
  57. package/dist/src/crypto.js +132 -0
  58. package/dist/src/daemon.d.ts +52 -0
  59. package/dist/src/daemon.js +142 -0
  60. package/dist/src/dashboard-cli.d.ts +2 -0
  61. package/dist/src/dashboard-cli.js +20 -0
  62. package/dist/src/disk.d.ts +110 -0
  63. package/dist/src/disk.js +169 -0
  64. package/dist/src/doctor.d.ts +11 -0
  65. package/dist/src/doctor.js +198 -0
  66. package/dist/src/enroll.d.ts +27 -0
  67. package/dist/src/enroll.js +136 -0
  68. package/dist/src/errors.d.ts +26 -0
  69. package/dist/src/errors.js +23 -0
  70. package/dist/src/heartbeat.d.ts +89 -0
  71. package/dist/src/heartbeat.js +120 -0
  72. package/dist/src/index-sync.d.ts +53 -0
  73. package/dist/src/index-sync.js +147 -0
  74. package/dist/src/leakscan.d.ts +48 -0
  75. package/dist/src/leakscan.js +222 -0
  76. package/dist/src/local-api.d.ts +132 -0
  77. package/dist/src/local-api.js +1757 -0
  78. package/dist/src/managed-chunks.d.ts +55 -0
  79. package/dist/src/managed-chunks.js +108 -0
  80. package/dist/src/mcp-install.d.ts +52 -0
  81. package/dist/src/mcp-install.js +140 -0
  82. package/dist/src/mcp.d.ts +1 -0
  83. package/dist/src/mcp.js +59 -0
  84. package/dist/src/migrate.d.ts +35 -0
  85. package/dist/src/migrate.js +88 -0
  86. package/dist/src/mnemonic.d.ts +60 -0
  87. package/dist/src/mnemonic.js +134 -0
  88. package/dist/src/net.d.ts +2 -0
  89. package/dist/src/net.js +16 -0
  90. package/dist/src/notify.d.ts +46 -0
  91. package/dist/src/notify.js +84 -0
  92. package/dist/src/offload.d.ts +117 -0
  93. package/dist/src/offload.js +331 -0
  94. package/dist/src/onboarding.d.ts +10 -0
  95. package/dist/src/onboarding.js +44 -0
  96. package/dist/src/packages.d.ts +126 -0
  97. package/dist/src/packages.js +114 -0
  98. package/dist/src/passkey.d.ts +26 -0
  99. package/dist/src/passkey.js +54 -0
  100. package/dist/src/password-lock.d.ts +19 -0
  101. package/dist/src/password-lock.js +156 -0
  102. package/dist/src/paths.d.ts +9 -0
  103. package/dist/src/paths.js +24 -0
  104. package/dist/src/providers/gcs.d.ts +133 -0
  105. package/dist/src/providers/gcs.js +235 -0
  106. package/dist/src/providers/gdrive.d.ts +156 -0
  107. package/dist/src/providers/gdrive.js +335 -0
  108. package/dist/src/providers/index.d.ts +45 -0
  109. package/dist/src/providers/index.js +74 -0
  110. package/dist/src/providers/s3.d.ts +174 -0
  111. package/dist/src/providers/s3.js +345 -0
  112. package/dist/src/providers/sigv4.d.ts +78 -0
  113. package/dist/src/providers/sigv4.js +112 -0
  114. package/dist/src/queue.d.ts +185 -0
  115. package/dist/src/queue.js +286 -0
  116. package/dist/src/recovery.d.ts +40 -0
  117. package/dist/src/recovery.js +132 -0
  118. package/dist/src/rehydrate.d.ts +43 -0
  119. package/dist/src/rehydrate.js +66 -0
  120. package/dist/src/restore.d.ts +34 -0
  121. package/dist/src/restore.js +80 -0
  122. package/dist/src/retention.d.ts +251 -0
  123. package/dist/src/retention.js +446 -0
  124. package/dist/src/rotate.d.ts +47 -0
  125. package/dist/src/rotate.js +95 -0
  126. package/dist/src/search.d.ts +147 -0
  127. package/dist/src/search.js +677 -0
  128. package/dist/src/secrets.d.ts +86 -0
  129. package/dist/src/secrets.js +220 -0
  130. package/dist/src/service.d.ts +73 -0
  131. package/dist/src/service.js +197 -0
  132. package/dist/src/share.d.ts +34 -0
  133. package/dist/src/share.js +68 -0
  134. package/dist/src/spool.d.ts +97 -0
  135. package/dist/src/spool.js +213 -0
  136. package/dist/src/start-tui.d.ts +17 -0
  137. package/dist/src/start-tui.js +113 -0
  138. package/dist/src/start.d.ts +75 -0
  139. package/dist/src/start.js +101 -0
  140. package/dist/src/storage-setup.d.ts +49 -0
  141. package/dist/src/storage-setup.js +222 -0
  142. package/dist/src/storage-targets.d.ts +40 -0
  143. package/dist/src/storage-targets.js +147 -0
  144. package/dist/src/stream-to-cloud.d.ts +76 -0
  145. package/dist/src/stream-to-cloud.js +820 -0
  146. package/dist/src/sync-rules.d.ts +85 -0
  147. package/dist/src/sync-rules.js +125 -0
  148. package/dist/src/trash.d.ts +15 -0
  149. package/dist/src/trash.js +63 -0
  150. package/dist/src/tui.d.ts +18 -0
  151. package/dist/src/tui.js +179 -0
  152. package/dist/src/types.d.ts +191 -0
  153. package/dist/src/types.js +3 -0
  154. package/dist/src/ui-server.d.ts +187 -0
  155. package/dist/src/ui-server.js +293 -0
  156. package/dist/src/ui.d.ts +41 -0
  157. package/dist/src/ui.js +102 -0
  158. package/dist/src/update.d.ts +30 -0
  159. package/dist/src/update.js +56 -0
  160. package/dist/src/upload.d.ts +46 -0
  161. package/dist/src/upload.js +80 -0
  162. package/dist/src/vault.d.ts +208 -0
  163. package/dist/src/vault.js +812 -0
  164. package/dist/src/watcher.d.ts +34 -0
  165. package/dist/src/watcher.js +121 -0
  166. package/dist/src/worker.d.ts +52 -0
  167. package/dist/src/worker.js +190 -0
  168. package/package.json +65 -0
  169. package/web/app.js +1372 -0
  170. package/web/index.html +476 -0
  171. package/web/rail.js +308 -0
  172. package/web/retention.html +17 -0
  173. package/web/rules-view.js +249 -0
  174. package/web/sessions-view.js +448 -0
  175. package/web/sessions.html +17 -0
  176. package/web/setup-api.js +181 -0
  177. package/web/setup-logic.js +394 -0
  178. package/web/setup.html +419 -0
  179. package/web/setup.js +697 -0
  180. package/web/style.css +990 -0
package/web/setup.html ADDED
@@ -0,0 +1,419 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <meta name="referrer" content="no-referrer">
7
+ <title>Sealkeep — first run</title>
8
+ <link rel="stylesheet" href="./style.css">
9
+ </head>
10
+ <body>
11
+ <aside>
12
+ <div class="brand"><span>⌑</span>Sealkeep</div>
13
+ <nav class="steps" aria-label="Setup steps">
14
+ <button type="button" class="step current" data-step="1" aria-current="step"><span class="glyph">1</span>What this does</button>
15
+ <button type="button" class="step locked" data-step="2" disabled><span class="glyph">2</span>How they're stored</button>
16
+ <button type="button" class="step locked" data-step="3" disabled><span class="glyph">3</span>Where they go</button>
17
+ <button type="button" class="step locked" data-step="4" disabled><span class="glyph">4</span>Your key</button>
18
+ <button type="button" class="step locked" data-step="5" disabled><span class="glyph">5</span>Done</button>
19
+ </nav>
20
+ <div class="privacy">
21
+ <b>Local only</b>
22
+ This page is served by Sealkeep itself, on 127.0.0.1. Nothing you type here is sent anywhere else.
23
+ </div>
24
+ </aside>
25
+
26
+ <main class="wizard">
27
+ <p class="pill busy" id="connection">Checking this machine</p>
28
+
29
+ <!-- 1 ─ What this does ─────────────────────────────────────────── -->
30
+ <section class="screen" id="screen-1">
31
+ <header>
32
+ <h1>What Sealkeep does with your sessions</h1>
33
+ <p class="lede">Three things happen to a finished session. Read them once — the rest of setup is just choosing how and where.</p>
34
+ </header>
35
+
36
+ <!-- Three cards on one spine, with the boundary strip cutting across at
37
+ the moment the file stops being readable. The dashboard reports that
38
+ line; this is where it gets explained. -->
39
+ <div class="trace" id="trace">
40
+ <div class="trace-row">
41
+ <b>Your agent finishes a session.</b>
42
+ <p>The transcript is an ordinary file in that agent's own folder, where anything on this machine can read it. Sealkeep has not touched it yet.</p>
43
+ <span class="state">readable</span>
44
+ </div>
45
+
46
+ <div class="boundary">
47
+ <span class="side">above: readable files on your disk</span>
48
+ <b>readable ends here</b>
49
+ <span class="side sealed">below: locked — only your key opens it</span>
50
+ </div>
51
+
52
+ <div class="trace-row sealed">
53
+ <b>Sealkeep locks a copy, here, on this machine.</b>
54
+ <p>Locked before it is stored anywhere, with a key that is made here and never leaves. Nothing readable ever leaves this machine — no session text, no file names, no key — so whoever holds the storage holds files they cannot open, and neither can we.</p>
55
+ <span class="state">locked</span>
56
+ </div>
57
+ <div class="trace-row sealed">
58
+ <b>The copy is stored. The original stays yours.</b>
59
+ <p>Copies go wherever you choose next — keeping them only on this disk is a real answer. The original file moves to your trash, never further, only after the copy is checked, and only once you allow it.</p>
60
+ <span class="state">reversible</span>
61
+ </div>
62
+ </div>
63
+
64
+ <div class="wizfoot">
65
+ <p class="note" id="foot-note-1">Takes about five minutes, or two if you keep everything on this machine.</p>
66
+ <div class="actions"><button type="button" class="action primary" data-go="2">Choose how archives are stored</button></div>
67
+ </div>
68
+ </section>
69
+
70
+ <!-- 2 ─ How archives are stored ───────────────────────────────── -->
71
+ <section class="screen" id="screen-2" hidden>
72
+ <header>
73
+ <h1>How your archives are stored</h1>
74
+ <p class="lede">Decided once, when the vault is created. A vault keeps one answer for life — changing it later means a new vault, and archiving everything again.</p>
75
+ </header>
76
+
77
+ <fieldset class="choices" id="mode-choices">
78
+ <legend class="sr-only">How your archives are stored</legend>
79
+ <label class="choice" for="m-sealed">
80
+ <input type="radio" id="m-sealed" name="storageMode" value="sealed" checked>
81
+ <div class="choice-body">
82
+ <b>Sealed</b>
83
+ <p class="note">Only you can ever read them. Each archive is locked on this machine before it is stored anywhere, so whoever holds the storage — your provider, us, anyone with your disk — holds files they cannot open.</p>
84
+ <p class="note">The default, and the right answer unless you specifically need readable files.</p>
85
+ </div>
86
+ </label>
87
+ <label class="choice warn" for="m-plain">
88
+ <input type="radio" id="m-plain" name="storageMode" value="plain">
89
+ <div class="choice-body">
90
+ <b>Plain</b>
91
+ <p class="note">Readable files — anyone with the storage can read them. Archiving, search and restore all work the same; what you give up is the lock. Wherever these files land, whoever can read the storage can read the sessions.</p>
92
+ <p class="note">Pick it only if you need the archives to be ordinary readable files more than you need them private.</p>
93
+ </div>
94
+ </label>
95
+ </fieldset>
96
+
97
+ <!-- A vault already exists: its mode is history, and offering the choice
98
+ again would be collecting an answer this page must throw away. -->
99
+ <div class="panel pad callout" id="mode-existing" hidden>
100
+ <h3>This machine already has a vault</h3>
101
+ <p>Its storage mode was decided when it was created and cannot be changed here. A different mode would mean a new vault in a new folder — the archives already made do not follow it.</p>
102
+ </div>
103
+
104
+ <div class="wizfoot">
105
+ <div class="actions"><button type="button" class="action" data-go="1">Back</button></div>
106
+ <div class="actions"><button type="button" class="action primary" data-go="3">Choose where they go</button></div>
107
+ </div>
108
+ </section>
109
+
110
+ <!-- 3 ─ Where archives go ─────────────────────────────────────── -->
111
+ <section class="screen" id="screen-3" hidden>
112
+ <header>
113
+ <h1>Where your archives go</h1>
114
+ <p class="lede">An archive is just a file. Sealkeep can leave it on this disk, or copy it to a bucket you own, or to storage we run.</p>
115
+ </header>
116
+
117
+ <!-- What each side of the price line actually buys, one sentence each,
118
+ before the answers that map onto it. -->
119
+ <div class="panel pad compare" id="compare-card">
120
+ <div class="compare-col">
121
+ <b>Free — storage you bring</b>
122
+ <p class="note">Your bucket, your keys, your bill — set up once, and it works forever.</p>
123
+ </div>
124
+ <div class="compare-col">
125
+ <b>Pro — storage we run</b>
126
+ <p class="note">We run the storage, every upload is verified durable, your machines connect from anywhere, you can see and revoke each device, and a sealed copy of your key waits in escrow only you can open.</p>
127
+ </div>
128
+ </div>
129
+
130
+ <details class="panel pad explain">
131
+ <summary>Never made a bucket before? Start here.</summary>
132
+ <p>A bucket is a folder that lives on someone else's disks and is reachable over the internet. You create one in your provider's console, give it a name that is unique to you, and generate a key pair that lets a program write into it. That is the whole idea.</p>
133
+ <p>Sealkeep only ever writes inside the prefix you name below, so with a sealed vault the provider stores bytes it cannot open. Budget about ten minutes the first time; the permissions screen is the slow part.</p>
134
+ <p class="note">Each provider's own instructions:
135
+ <a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/creating-bucket.html" target="_blank" rel="noopener noreferrer">Amazon S3</a> ·
136
+ <a href="https://developers.cloudflare.com/r2/buckets/create-buckets/" target="_blank" rel="noopener noreferrer">Cloudflare R2</a> ·
137
+ <a href="https://www.backblaze.com/docs/cloud-storage-create-and-manage-buckets" target="_blank" rel="noopener noreferrer">Backblaze B2</a> ·
138
+ <a href="https://cloud.google.com/storage/docs/creating-buckets" target="_blank" rel="noopener noreferrer">Google Cloud Storage</a>
139
+ </p>
140
+ </details>
141
+
142
+ <fieldset class="choices" id="provider-choices">
143
+ <legend class="sr-only">Where archives go</legend>
144
+ </fieldset>
145
+
146
+ <!-- Which vendor, asked only of someone who has already said they want
147
+ their own bucket. Four vendors listed beside "keep it here" made an
148
+ implementation detail look like a peer of the decision. -->
149
+ <fieldset class="choices" id="own-providers" hidden>
150
+ <legend class="sr-only">Which bucket</legend>
151
+ </fieldset>
152
+
153
+ <!-- Shown only when managed storage is chosen, because that is the one
154
+ choice here that genuinely needs an account: there is a bucket to
155
+ provision and a bill to attach it to. Everything else on this page
156
+ works signed out, which is what makes asking here defensible. -->
157
+ <div class="panel pad" id="account-panel" hidden>
158
+ <h3>Your Sealkeep account</h3>
159
+ <p class="note">
160
+ Managed storage runs on an account. Create one or sign in below, then press
161
+ <b>Show a pairing code</b> on that page and bring the code back here. Nothing on this page is sent
162
+ anywhere until you do.
163
+ </p>
164
+ <div class="row">
165
+ <div class="field">
166
+ <label for="f-enroll-email">The email on your account</label>
167
+ <input id="f-enroll-email" name="enrollEmail" type="email" autocomplete="email" spellcheck="false" placeholder="you@example.com">
168
+ </div>
169
+ <div class="field">
170
+ <label for="f-enroll-code">Pairing code</label>
171
+ <input id="f-enroll-code" name="enrollCode" type="text" autocomplete="off" spellcheck="false" placeholder="paste the code">
172
+ </div>
173
+ </div>
174
+ <p class="note error" id="e-enroll-code" hidden></p>
175
+ <!-- Both links name a tab, not just the section, so nobody lands on the
176
+ page and has to work out which half they were sent for. -->
177
+ <div class="actions">
178
+ <a class="action primary" href="https://shared.spala.ai/p04946/#signup" target="_blank" rel="noreferrer noopener">Create an account</a>
179
+ <a class="action" href="https://shared.spala.ai/p04946/#signin" target="_blank" rel="noreferrer noopener">I already have one — sign in</a>
180
+ </div>
181
+ <p class="note">The pairing code is single use, and it is not your recovery phrase.</p>
182
+ <div class="actions">
183
+ <button type="button" class="action" id="enroll-go">Add this machine</button>
184
+ </div>
185
+ <p class="note" id="enroll-result"></p>
186
+ <p class="note">
187
+ This links the machine to your plan and storage. It is not your recovery phrase and it does not
188
+ let anyone open a sealed archive — including us.
189
+ </p>
190
+ </div>
191
+
192
+ <form class="panel pad" id="storage-form" hidden>
193
+ <h3 id="storage-form-title">Bucket</h3>
194
+ <div class="row">
195
+ <div class="field">
196
+ <label for="f-bucket">Bucket name</label>
197
+ <input id="f-bucket" name="bucket" type="text" autocomplete="off" spellcheck="false" placeholder="my-vaultline-archives">
198
+ <p class="note error" id="e-bucket" hidden></p>
199
+ </div>
200
+ <div class="field">
201
+ <label for="f-prefix">Folder inside it</label>
202
+ <input id="f-prefix" name="prefix" type="text" autocomplete="off" spellcheck="false" value="vaultline">
203
+ <p class="note error" id="e-prefix" hidden></p>
204
+ </div>
205
+ </div>
206
+ <div id="provider-fields"></div>
207
+ <p class="note">Credentials go straight into this machine's keychain. No Sealkeep endpoint reads them back — not masked, not partially, not for this page.</p>
208
+ <!-- Credentials are the step most likely to be wrong, and every way of
209
+ being wrong otherwise surfaces hours later in a background upload
210
+ nobody is watching. This writes a small object and reads it back
211
+ while the person is still looking at the form. -->
212
+ <div class="actions" style="margin-top:.9rem">
213
+ <button type="button" class="action" id="storage-test">Test this bucket</button>
214
+ <span class="note" id="storage-test-result"></span>
215
+ </div>
216
+ </form>
217
+
218
+ <div class="wizfoot">
219
+ <div class="actions"><button type="button" class="action" data-go="2">Back</button></div>
220
+ <div class="actions">
221
+ <p class="note error" id="storage-error" hidden></p>
222
+ <button type="button" class="action primary" data-go="4">Continue to your key</button>
223
+ </div>
224
+ </div>
225
+ </section>
226
+
227
+ <!-- 4 ─ Your key ──────────────────────────────────────────────── -->
228
+ <section class="screen" id="screen-4" hidden>
229
+ <header>
230
+ <h1>Your key</h1>
231
+ <p class="lede" id="phrase-lede">The part that decides who can ever open these archives.</p>
232
+ </header>
233
+
234
+ <!-- The free paths: a password first, because it is the kind of key people
235
+ already know how to keep. The phrase still exists — offered after the
236
+ vault does, as the extra rather than the toll. -->
237
+ <div id="password-before" hidden>
238
+ <div class="panel pad callout">
239
+ <p>This vault will open with a <b>password</b>, here on this machine. Choose it below and the vault is created the moment you do.</p>
240
+ <p>A recovery phrase is generated at the same time. After the vault exists you can write it down — it opens the vault anywhere, even without this password — or skip it and rely on the password alone.</p>
241
+ </div>
242
+ <form class="panel pad" id="password-form">
243
+ <h3>Choose the password</h3>
244
+ <div class="row">
245
+ <div class="field">
246
+ <label for="f-password">Vault password</label>
247
+ <input id="f-password" name="password" type="password" autocomplete="new-password" spellcheck="false">
248
+ <p class="note" id="password-hint">At least 8 characters. Length is what makes it strong.</p>
249
+ </div>
250
+ <div class="field">
251
+ <label for="f-password-again">The same password, again</label>
252
+ <input id="f-password-again" name="passwordAgain" type="password" autocomplete="new-password" spellcheck="false">
253
+ </div>
254
+ </div>
255
+ <p class="note error" id="password-error" hidden></p>
256
+ <div class="wizfoot">
257
+ <div class="actions"><button type="button" class="action" data-go="3">Back</button></div>
258
+ <div class="actions"><button type="submit" class="action primary" id="create-with-password">Create the vault</button></div>
259
+ </div>
260
+ </form>
261
+ </div>
262
+
263
+ <div id="password-after" hidden>
264
+ <div class="panel pad callout">
265
+ <h3>Your vault is ready</h3>
266
+ <p>It unlocks with your password on this machine. If you ever lose the password, the phrase below is the only other way in — which is a good reason to spend a minute on it now.</p>
267
+ </div>
268
+ <details class="panel pad explain" id="phrase-fold">
269
+ <summary>Prefer a recovery phrase too?</summary>
270
+ <p id="fold-note"></p>
271
+ <ol class="phrase" id="fold-phrase-words"></ol>
272
+ </details>
273
+ <div class="wizfoot">
274
+ <p class="note">Skipping the phrase is fine — your password already opens this vault.</p>
275
+ <div class="actions"><button type="button" class="action primary" id="finish-password">Finish setup</button></div>
276
+ </div>
277
+ </div>
278
+
279
+ <!-- The pro path: the phrase ceremony, unchanged. The warning has to
280
+ arrive before the secret does. -->
281
+ <div id="phrase-before" hidden>
282
+ <div class="panel pad callout warn">
283
+ <p>Sealkeep is about to generate <b>24 words</b>. Those words are the key to every archive it will ever make here.</p>
284
+ <ul class="plain">
285
+ <li><b>They are shown once, on this page, and never again.</b> No command, no endpoint and no request to us can produce them a second time.</li>
286
+ <li><b>Close this page before writing them down and they are gone.</b> The vault would have to be created again from nothing.</li>
287
+ <li><b>Nobody else holds a copy.</b> Not your disk in readable form, not your storage provider, not us. That is the point, and it is also the risk.</li>
288
+ </ul>
289
+ <p>Have paper or a password manager open before you click.</p>
290
+ </div>
291
+ <div class="wizfoot">
292
+ <div class="actions"><button type="button" class="action" data-go="3">Back</button></div>
293
+ <div class="actions">
294
+ <p class="note error" id="vault-error" hidden></p>
295
+ <button type="button" class="action primary" id="generate">Create the vault and show my phrase</button>
296
+ </div>
297
+ </div>
298
+ </div>
299
+
300
+ <!-- After: the words, then the two gates that stand between them and the next screen. -->
301
+ <div id="phrase-after" hidden>
302
+ <div class="boundary">
303
+ <span class="side">generated on this machine</span>
304
+ <b>the only key</b>
305
+ <span class="side sealed">shown once</span>
306
+ </div>
307
+
308
+ <ol class="phrase" id="phrase-words" aria-label="Your 24-word recovery phrase"></ol>
309
+
310
+ <div class="actions phrase-actions">
311
+ <button type="button" class="action" id="copy-phrase">Copy the phrase</button>
312
+ <button type="button" class="action" id="print-phrase">Print this page</button>
313
+ <p class="note" id="copy-note">A clipboard is not private and outlives this page. Paste it, then copy something else.</p>
314
+ </div>
315
+
316
+ <form class="panel pad" id="confirm-form">
317
+ <h3>Confirm you have it</h3>
318
+ <p class="note">Type these back from what you wrote down, not from the list above. This is checked here on this page and is never sent anywhere.</p>
319
+ <div class="row" id="verify-fields"></div>
320
+ <p class="note error" id="verify-error" hidden></p>
321
+ <label class="opt" for="f-understood">
322
+ <input type="checkbox" id="f-understood">
323
+ <span>I have written down all 24 words, in order. I understand this page is the only place they will ever appear.</span>
324
+ </label>
325
+ <div class="wizfoot">
326
+ <p class="note" id="confirm-note">Continue unlocks once both of those are true.</p>
327
+ <div class="actions"><button type="submit" class="action primary" id="finish" disabled>Finish setup</button></div>
328
+ </div>
329
+ </form>
330
+ </div>
331
+
332
+ <!-- A vault already exists: never offer a second phrase, and never pretend one can be reshown. -->
333
+ <div id="phrase-existing" hidden>
334
+ <div class="panel pad callout">
335
+ <h3>This machine already has a vault</h3>
336
+ <p id="existing-detail">It was created earlier, and its recovery phrase was shown once at that moment.</p>
337
+ <p>It cannot be shown again — not by this page, not by any command. If you have it written down, you are set: continue, and Sealkeep will save your storage settings against the vault that is already here.</p>
338
+ <p>If you do not have it, the archives already sealed with it stay sealed. A new phrase means a new vault in a new data directory, and the old archives do not come with it.</p>
339
+ </div>
340
+ <div class="wizfoot">
341
+ <div class="actions"><button type="button" class="action" data-go="3">Back</button></div>
342
+ <div class="actions"><button type="button" class="action primary" id="finish-existing">Finish setup</button></div>
343
+ </div>
344
+ </div>
345
+ </section>
346
+
347
+ <!-- 5 ─ Done ──────────────────────────────────────────────────── -->
348
+ <section class="screen" id="screen-5" hidden>
349
+ <header>
350
+ <h1 id="done-headline">Sealkeep is running</h1>
351
+ <p class="lede">Here is what is live on this machine.</p>
352
+ </header>
353
+
354
+ <div class="panel" id="done-list"></div>
355
+
356
+ <div class="panel pad callout">
357
+ <h3>What happens next</h3>
358
+ <p id="next-detail">Nothing else is needed. The next time one of your agents finishes a session, the transcript is queued here and archived. It shows up on the dashboard within a few minutes.</p>
359
+ <h3>Two things worth doing today</h3>
360
+ <p><span id="today-key">Check that your recovery phrase is somewhere you will still have it in a year — a drawer beats a laptop.</span> Then open <a href="./index.html#rules">Retention</a> and decide when Sealkeep may move originals to your trash. Until you say so, it never does.</p>
361
+ </div>
362
+
363
+ <div class="wizfoot">
364
+ <p class="note">You can close this page. Sealkeep keeps running without it.</p>
365
+ <div class="actions">
366
+ <a class="action" href="./index.html#rules">Retention settings</a>
367
+ <a class="action primary" href="./index.html">Open the dashboard</a>
368
+ </div>
369
+ </div>
370
+ </section>
371
+
372
+ <!-- Shown instead of a blank page when the machine cannot answer. -->
373
+ <!-- The token lives in sessionStorage, which belongs to one browser. Opening
374
+ this address in a second one arrives with nothing, and telling that person
375
+ to "run vaultline ui" is both a terminal instruction and the wrong one:
376
+ it would open their default browser, not the one they are looking at. So
377
+ the page offers the way in that works from here. -->
378
+ <section class="screen" id="screen-gate" hidden>
379
+ <header>
380
+ <h1>Sign this browser in</h1>
381
+ <p class="lede">Sealkeep is running on this machine — this browser just has not been given its key yet. That happens automatically in whichever browser <code>vaultline ui</code> opened.</p>
382
+ </header>
383
+ <div class="panel pad">
384
+ <p class="note">Paste the token from <code id="gate-token-path">~/.vaultline/local-api-token</code>. It is a local file; nothing here is sent anywhere.</p>
385
+ <form id="gate-form">
386
+ <div class="field">
387
+ <label for="gate-token">Local API token</label>
388
+ <input id="gate-token" type="password" autocomplete="off" spellcheck="false" placeholder="paste the token">
389
+ </div>
390
+ <div class="actions"><button type="submit" class="action primary">Connect this browser</button></div>
391
+ </form>
392
+ <p class="note error" id="gate-error" hidden></p>
393
+ <p class="note">On macOS or Linux: <code>cat ~/.vaultline/local-api-token</code></p>
394
+ </div>
395
+ </section>
396
+
397
+ <section class="screen" id="screen-unavailable" hidden>
398
+ <header>
399
+ <h1>Setup cannot run here yet</h1>
400
+ <p class="lede" id="unavailable-detail"></p>
401
+ </header>
402
+ <div class="panel pad callout">
403
+ <h3>What to do instead</h3>
404
+ <p>Update to a build that has the setup endpoints: <code>npm i -g vaultline@latest</code>, then run <code>vaultline</code> again.</p>
405
+ <p>Or set the same things up from a terminal — the wizard is a second way in, never the only one: <code>vaultline setup</code>, <code>vaultline storage configure</code>.</p>
406
+ <p>Already have a vault? The <a href="./index.html">dashboard</a> works regardless.</p>
407
+ </div>
408
+ </section>
409
+
410
+ <footer>
411
+ <div>
412
+ <b id="footer-title">Sealed before it leaves</b>
413
+ <span>Encryption happens on this machine, before any provider or network sees a byte.</span>
414
+ </div>
415
+ </footer>
416
+ </main>
417
+ <script type="module" src="./setup.js"></script>
418
+ </body>
419
+ </html>