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.
- package/ARCHITECTURE.md +201 -0
- package/CHANGELOG.md +218 -0
- package/CONTROL_PLANE.md +86 -0
- package/LICENSE +34 -0
- package/README.md +249 -0
- package/THIRD_PARTY.md +22 -0
- package/THREAT_MODEL.md +107 -0
- package/dist/packages/vaultline-crypto/src/aead.d.ts +12 -0
- package/dist/packages/vaultline-crypto/src/aead.js +24 -0
- package/dist/packages/vaultline-crypto/src/chunk-access.d.ts +39 -0
- package/dist/packages/vaultline-crypto/src/chunk-access.js +93 -0
- package/dist/packages/vaultline-crypto/src/envelope.d.ts +71 -0
- package/dist/packages/vaultline-crypto/src/envelope.js +188 -0
- package/dist/packages/vaultline-crypto/src/format.d.ts +106 -0
- package/dist/packages/vaultline-crypto/src/format.js +43 -0
- package/dist/packages/vaultline-crypto/src/index.d.ts +5 -0
- package/dist/packages/vaultline-crypto/src/index.js +5 -0
- package/dist/packages/vaultline-crypto/src/recipients.d.ts +42 -0
- package/dist/packages/vaultline-crypto/src/recipients.js +129 -0
- package/dist/packages/vaultline-crypto/src/sha256-stream.d.ts +41 -0
- package/dist/packages/vaultline-crypto/src/sha256-stream.js +206 -0
- package/dist/packages/vaultline-crypto/src/stream.d.ts +139 -0
- package/dist/packages/vaultline-crypto/src/stream.js +477 -0
- package/dist/site/index.html +1542 -0
- package/dist/site.zip +0 -0
- package/dist/src/activity.d.ts +22 -0
- package/dist/src/activity.js +52 -0
- package/dist/src/adapters.d.ts +212 -0
- package/dist/src/adapters.js +533 -0
- package/dist/src/audit.d.ts +24 -0
- package/dist/src/audit.js +41 -0
- package/dist/src/autopilot.d.ts +77 -0
- package/dist/src/autopilot.js +148 -0
- package/dist/src/bip39-wordlist.d.ts +15 -0
- package/dist/src/bip39-wordlist.js +272 -0
- package/dist/src/branding.d.ts +31 -0
- package/dist/src/branding.js +31 -0
- package/dist/src/chunk-store.d.ts +142 -0
- package/dist/src/chunk-store.js +502 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +2035 -0
- package/dist/src/cloud.d.ts +434 -0
- package/dist/src/cloud.js +851 -0
- package/dist/src/control-plane/auth.d.ts +62 -0
- package/dist/src/control-plane/auth.js +123 -0
- package/dist/src/control-plane/server.d.ts +31 -0
- package/dist/src/control-plane/server.js +263 -0
- package/dist/src/control-plane/store.d.ts +101 -0
- package/dist/src/control-plane/store.js +82 -0
- package/dist/src/control-plane-cli.d.ts +2 -0
- package/dist/src/control-plane-cli.js +37 -0
- package/dist/src/control-plane-server.d.ts +10 -0
- package/dist/src/control-plane-server.js +11 -0
- package/dist/src/control-plane.d.ts +78 -0
- package/dist/src/control-plane.js +61 -0
- package/dist/src/crypto.d.ts +56 -0
- package/dist/src/crypto.js +132 -0
- package/dist/src/daemon.d.ts +52 -0
- package/dist/src/daemon.js +142 -0
- package/dist/src/dashboard-cli.d.ts +2 -0
- package/dist/src/dashboard-cli.js +20 -0
- package/dist/src/disk.d.ts +110 -0
- package/dist/src/disk.js +169 -0
- package/dist/src/doctor.d.ts +11 -0
- package/dist/src/doctor.js +198 -0
- package/dist/src/enroll.d.ts +27 -0
- package/dist/src/enroll.js +136 -0
- package/dist/src/errors.d.ts +26 -0
- package/dist/src/errors.js +23 -0
- package/dist/src/heartbeat.d.ts +89 -0
- package/dist/src/heartbeat.js +120 -0
- package/dist/src/index-sync.d.ts +53 -0
- package/dist/src/index-sync.js +147 -0
- package/dist/src/leakscan.d.ts +48 -0
- package/dist/src/leakscan.js +222 -0
- package/dist/src/local-api.d.ts +132 -0
- package/dist/src/local-api.js +1757 -0
- package/dist/src/managed-chunks.d.ts +55 -0
- package/dist/src/managed-chunks.js +108 -0
- package/dist/src/mcp-install.d.ts +52 -0
- package/dist/src/mcp-install.js +140 -0
- package/dist/src/mcp.d.ts +1 -0
- package/dist/src/mcp.js +59 -0
- package/dist/src/migrate.d.ts +35 -0
- package/dist/src/migrate.js +88 -0
- package/dist/src/mnemonic.d.ts +60 -0
- package/dist/src/mnemonic.js +134 -0
- package/dist/src/net.d.ts +2 -0
- package/dist/src/net.js +16 -0
- package/dist/src/notify.d.ts +46 -0
- package/dist/src/notify.js +84 -0
- package/dist/src/offload.d.ts +117 -0
- package/dist/src/offload.js +331 -0
- package/dist/src/onboarding.d.ts +10 -0
- package/dist/src/onboarding.js +44 -0
- package/dist/src/packages.d.ts +126 -0
- package/dist/src/packages.js +114 -0
- package/dist/src/passkey.d.ts +26 -0
- package/dist/src/passkey.js +54 -0
- package/dist/src/password-lock.d.ts +19 -0
- package/dist/src/password-lock.js +156 -0
- package/dist/src/paths.d.ts +9 -0
- package/dist/src/paths.js +24 -0
- package/dist/src/providers/gcs.d.ts +133 -0
- package/dist/src/providers/gcs.js +235 -0
- package/dist/src/providers/gdrive.d.ts +156 -0
- package/dist/src/providers/gdrive.js +335 -0
- package/dist/src/providers/index.d.ts +45 -0
- package/dist/src/providers/index.js +74 -0
- package/dist/src/providers/s3.d.ts +174 -0
- package/dist/src/providers/s3.js +345 -0
- package/dist/src/providers/sigv4.d.ts +78 -0
- package/dist/src/providers/sigv4.js +112 -0
- package/dist/src/queue.d.ts +185 -0
- package/dist/src/queue.js +286 -0
- package/dist/src/recovery.d.ts +40 -0
- package/dist/src/recovery.js +132 -0
- package/dist/src/rehydrate.d.ts +43 -0
- package/dist/src/rehydrate.js +66 -0
- package/dist/src/restore.d.ts +34 -0
- package/dist/src/restore.js +80 -0
- package/dist/src/retention.d.ts +251 -0
- package/dist/src/retention.js +446 -0
- package/dist/src/rotate.d.ts +47 -0
- package/dist/src/rotate.js +95 -0
- package/dist/src/search.d.ts +147 -0
- package/dist/src/search.js +677 -0
- package/dist/src/secrets.d.ts +86 -0
- package/dist/src/secrets.js +220 -0
- package/dist/src/service.d.ts +73 -0
- package/dist/src/service.js +197 -0
- package/dist/src/share.d.ts +34 -0
- package/dist/src/share.js +68 -0
- package/dist/src/spool.d.ts +97 -0
- package/dist/src/spool.js +213 -0
- package/dist/src/start-tui.d.ts +17 -0
- package/dist/src/start-tui.js +113 -0
- package/dist/src/start.d.ts +75 -0
- package/dist/src/start.js +101 -0
- package/dist/src/storage-setup.d.ts +49 -0
- package/dist/src/storage-setup.js +222 -0
- package/dist/src/storage-targets.d.ts +40 -0
- package/dist/src/storage-targets.js +147 -0
- package/dist/src/stream-to-cloud.d.ts +76 -0
- package/dist/src/stream-to-cloud.js +820 -0
- package/dist/src/sync-rules.d.ts +85 -0
- package/dist/src/sync-rules.js +125 -0
- package/dist/src/trash.d.ts +15 -0
- package/dist/src/trash.js +63 -0
- package/dist/src/tui.d.ts +18 -0
- package/dist/src/tui.js +179 -0
- package/dist/src/types.d.ts +191 -0
- package/dist/src/types.js +3 -0
- package/dist/src/ui-server.d.ts +187 -0
- package/dist/src/ui-server.js +293 -0
- package/dist/src/ui.d.ts +41 -0
- package/dist/src/ui.js +102 -0
- package/dist/src/update.d.ts +30 -0
- package/dist/src/update.js +56 -0
- package/dist/src/upload.d.ts +46 -0
- package/dist/src/upload.js +80 -0
- package/dist/src/vault.d.ts +208 -0
- package/dist/src/vault.js +812 -0
- package/dist/src/watcher.d.ts +34 -0
- package/dist/src/watcher.js +121 -0
- package/dist/src/worker.d.ts +52 -0
- package/dist/src/worker.js +190 -0
- package/package.json +65 -0
- package/web/app.js +1372 -0
- package/web/index.html +476 -0
- package/web/rail.js +308 -0
- package/web/retention.html +17 -0
- package/web/rules-view.js +249 -0
- package/web/sessions-view.js +448 -0
- package/web/sessions.html +17 -0
- package/web/setup-api.js +181 -0
- package/web/setup-logic.js +394 -0
- package/web/setup.html +419 -0
- package/web/setup.js +697 -0
- package/web/style.css +990 -0
package/web/index.html
ADDED
|
@@ -0,0 +1,476 @@
|
|
|
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</title>
|
|
8
|
+
<link rel="stylesheet" href="./style.css">
|
|
9
|
+
<style>
|
|
10
|
+
/* Memory pool: the project column. The name answers "which repo", the dim
|
|
11
|
+
folder under it answers "which one exactly" — needed because two folders
|
|
12
|
+
can share a last segment, and a Codex row's path column is a rollout
|
|
13
|
+
filename that says nothing about where it ran. */
|
|
14
|
+
td.proj-cell { white-space: nowrap; }
|
|
15
|
+
td.proj-cell .name { display: block; }
|
|
16
|
+
td.proj-cell .name.filterable { cursor: pointer; text-decoration: underline dotted var(--rule); text-underline-offset: 3px; }
|
|
17
|
+
td.proj-cell .where {
|
|
18
|
+
display: block;
|
|
19
|
+
max-width: 22ch;
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
text-overflow: ellipsis;
|
|
22
|
+
color: var(--soft);
|
|
23
|
+
font-size: 11px;
|
|
24
|
+
}
|
|
25
|
+
/* The session column shows the file's name, not its address: a full path is
|
|
26
|
+
ninety characters of which a person reads none. Click for the whole thing
|
|
27
|
+
(to copy, or to be sure), click again to fold it away. */
|
|
28
|
+
td.path .short {
|
|
29
|
+
display: inline-block;
|
|
30
|
+
max-width: 34ch;
|
|
31
|
+
overflow: hidden;
|
|
32
|
+
text-overflow: ellipsis;
|
|
33
|
+
vertical-align: bottom;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
}
|
|
36
|
+
td.path .full { cursor: pointer; overflow-wrap: anywhere; }
|
|
37
|
+
/* Rules view: the armed step of the design system's destructive pattern —
|
|
38
|
+
the same button, filled with the warning colour while one click from acting. */
|
|
39
|
+
#copies-body .del[data-armed=yes] { background: var(--warn); border-color: var(--warn); color: var(--card); }
|
|
40
|
+
#copies-wrap { overflow-x: auto; }
|
|
41
|
+
#copies-body td.num { white-space: nowrap; }
|
|
42
|
+
#copies-body .rowmsg { display: block; max-width: 34ch; font-size: .8rem; color: var(--warn); }
|
|
43
|
+
#copies-body .lastmark { font-size: .75rem; color: var(--soft); border: 1px solid var(--rule); border-radius: 3px; padding: 0 .35em; margin-left: .5em; white-space: nowrap; }
|
|
44
|
+
/* The sentence an armed destructive button shows: what goes, what remains.
|
|
45
|
+
Warn-coloured because it precedes a warn-worthy click, never decoration. */
|
|
46
|
+
.act-note { display: block; max-width: 44ch; font-size: .78rem; color: var(--warn); margin-top: .3rem; }
|
|
47
|
+
/* The vault's folder shape: project headers you open, chains you unfold. */
|
|
48
|
+
.group-row td { background: var(--card); border-top: 1px solid var(--rule); }
|
|
49
|
+
.group-row b { font-family: var(--mono); }
|
|
50
|
+
.chain-row td { color: var(--soft); }
|
|
51
|
+
.chain-row td:nth-child(4)::before { content: "└ "; color: var(--rule); }
|
|
52
|
+
.chain-fold td { padding-top: 0; }
|
|
53
|
+
</style>
|
|
54
|
+
</head>
|
|
55
|
+
<body>
|
|
56
|
+
<aside>
|
|
57
|
+
<div class="brand"><span>⌑</span>Sealkeep</div>
|
|
58
|
+
|
|
59
|
+
<!-- The identity strip is always first (design-system: components/cards.html):
|
|
60
|
+
who this vault belongs to, what they pay for, and how much of it is used —
|
|
61
|
+
before any navigation. Populated by app.js; hidden until it can be. -->
|
|
62
|
+
<!-- A fixed skeleton, deliberately: every slot exists before the data does,
|
|
63
|
+
values overwrite placeholders in place, and the nav below never moves.
|
|
64
|
+
The card used to appear whole once /v1/status answered — which read as
|
|
65
|
+
the rail jumping on every load. -->
|
|
66
|
+
<div class="acct" id="acct-card">
|
|
67
|
+
<div class="acct-plan" id="acct-plan"><span id="acct-plan-name">—</span></div>
|
|
68
|
+
<div class="acct-email" id="acct-email">…</div>
|
|
69
|
+
<div class="acct-meter" id="acct-meter"><div class="acct-fill" id="acct-fill" style="width:0%"></div></div>
|
|
70
|
+
<div class="acct-usage" id="acct-usage">—</div>
|
|
71
|
+
<ul class="acct-facts">
|
|
72
|
+
<li id="acct-phrase"><span class="dot"></span><span class="acct-fact-text" id="acct-phrase-text">—</span></li>
|
|
73
|
+
<li id="acct-storage"><span class="dot"></span><span class="acct-fact-text" id="acct-storage-text">—</span></li>
|
|
74
|
+
<li id="acct-disk"><span class="dot"></span><span class="acct-fact-text" id="acct-disk-text">—</span></li>
|
|
75
|
+
</ul>
|
|
76
|
+
<div class="acct-door-slot" id="acct-door-slot"></div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<nav>
|
|
80
|
+
<button class="active" data-view="overview"><span class="glyph">▦</span>Overview</button>
|
|
81
|
+
<!-- A view like the rest: it was its own page once, which made every trip
|
|
82
|
+
between "pick what to archive" and anywhere else a full reload. The
|
|
83
|
+
list mounts on first entry and stays mounted — switching back is
|
|
84
|
+
instant. -->
|
|
85
|
+
<button data-view="sessions"><span class="glyph">◱</span>Memory pool</button>
|
|
86
|
+
<button data-view="queue"><span class="glyph">◴</span>Queue<span class="count" id="count-queue"></span></button>
|
|
87
|
+
<button data-view="archives"><span class="glyph">▣</span>My vault<span class="count" id="count-archives"></span></button>
|
|
88
|
+
<button data-view="settings"><span class="glyph">◧</span>Settings</button>
|
|
89
|
+
<!-- Badged like Queue and Archives. It was the one nav item without a
|
|
90
|
+
count, on a product where "if this machine is lost, so is every
|
|
91
|
+
archive on it" is a Health warning. -->
|
|
92
|
+
<button data-view="health"><span class="glyph">⚙</span>Health<span class="count" id="count-health"></span></button>
|
|
93
|
+
</nav>
|
|
94
|
+
|
|
95
|
+
<div class="privacy">
|
|
96
|
+
<b>Local only</b>
|
|
97
|
+
This page reads from the agent on 127.0.0.1. It cannot decrypt an archive.
|
|
98
|
+
</div>
|
|
99
|
+
</aside>
|
|
100
|
+
|
|
101
|
+
<main>
|
|
102
|
+
<header>
|
|
103
|
+
<h1 id="headline">Your agent history</h1>
|
|
104
|
+
<p class="lede" id="lede">Reading live state from the Sealkeep agent on this machine.</p>
|
|
105
|
+
<p class="pill busy" id="connection">Connecting</p>
|
|
106
|
+
<p class="pill" id="daemon" hidden></p>
|
|
107
|
+
</header>
|
|
108
|
+
|
|
109
|
+
<section id="gate" class="panel gate pad" hidden>
|
|
110
|
+
<h3>Local token required</h3>
|
|
111
|
+
<p class="note">This browser has not been given its key yet. Paste the token from <code id="token-path">~/.vaultline/local-api-token</code> — a local file, so nothing here is sent anywhere. The browser <code>vaultline ui</code> opened is signed in already.</p>
|
|
112
|
+
<form id="token-form">
|
|
113
|
+
<input id="token-input" type="password" autocomplete="off" spellcheck="false" placeholder="local API token" aria-label="Local API token">
|
|
114
|
+
<button type="submit" class="action">Connect</button>
|
|
115
|
+
</form>
|
|
116
|
+
<p id="gate-error" class="error" hidden></p>
|
|
117
|
+
</section>
|
|
118
|
+
|
|
119
|
+
<div id="app" hidden>
|
|
120
|
+
<section id="view-overview" class="view">
|
|
121
|
+
<div class="metrics">
|
|
122
|
+
<div class="metric"><h3>Sessions preserved</h3><b id="m-archives">–</b><small id="m-bytes"></small></div>
|
|
123
|
+
<div class="metric"><h3>Waiting</h3><b id="m-queue">–</b><small id="m-queue-detail"></small></div>
|
|
124
|
+
<div class="metric"><h3>Off this machine</h3><b id="m-remote" class="small">–</b><small id="m-remote-hint"></small></div>
|
|
125
|
+
<div class="metric"><h3>Disk you can reclaim</h3><b id="m-reclaim">–</b><small id="m-policy"></small></div>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<!-- The signature device: the same rule the product is named after, with live numbers on both sides. -->
|
|
129
|
+
<div class="boundary">
|
|
130
|
+
<span class="side" id="boundary-above"></span>
|
|
131
|
+
<b>plaintext ends here</b>
|
|
132
|
+
<span class="side sealed" id="boundary-below"></span>
|
|
133
|
+
</div>
|
|
134
|
+
<p class="note" id="boundary-note">Everything below this line is ciphertext. Nothing above it leaves this machine.</p>
|
|
135
|
+
|
|
136
|
+
<h2>Agents</h2>
|
|
137
|
+
<div class="panel" id="agents"></div>
|
|
138
|
+
</section>
|
|
139
|
+
|
|
140
|
+
<section id="view-queue" class="view" hidden>
|
|
141
|
+
<!-- The machine's side of the product: what is sealing now, what waits,
|
|
142
|
+
what uploads, and what the automatic rules would reclaim. The vault
|
|
143
|
+
is the library; this is the conveyor. -->
|
|
144
|
+
<div class="panel pad">
|
|
145
|
+
<div class="row">
|
|
146
|
+
<div>
|
|
147
|
+
<b id="retention-headline" style="font-family:var(--mono)">–</b>
|
|
148
|
+
<p class="note" id="retention-summary"></p>
|
|
149
|
+
</div>
|
|
150
|
+
<div class="actions">
|
|
151
|
+
<button id="upload" class="action" type="button">Upload pending</button>
|
|
152
|
+
<button id="reclaim" class="action danger" type="button" disabled>Move sources to trash</button>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
<div id="upload-progress" hidden>
|
|
156
|
+
<div class="meter"><div class="meter-fill" id="upload-fill" style="width:0%"></div></div>
|
|
157
|
+
<div class="row" style="align-items:baseline">
|
|
158
|
+
<p class="note" id="upload-line" style="margin:0"></p>
|
|
159
|
+
<button id="upload-cancel" class="action" type="button">Stop after this archive</button>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
<p class="note">The button reclaims what the rules allow: a verified remote copy, the grace period passed, nothing using the file. Any vault row can also do it one at a time. Reclaimed files go to your trash — Sealkeep never deletes.</p>
|
|
163
|
+
<p class="note"><a href="#rules">Edit the rules that decide what becomes eligible →</a></p>
|
|
164
|
+
</div>
|
|
165
|
+
<div class="panel pad" id="automation-panel" style="margin-top:var(--gap)" hidden></div>
|
|
166
|
+
<div class="panel" id="queue-panel" style="margin-top:var(--gap)"></div>
|
|
167
|
+
</section>
|
|
168
|
+
|
|
169
|
+
<section id="view-archives" class="view" hidden>
|
|
170
|
+
<!-- The library: folders of sessions, every verb on its row. The
|
|
171
|
+
automatic side (uploads, rules-driven reclaim) lives with the Queue,
|
|
172
|
+
where work in motion belongs. -->
|
|
173
|
+
<div class="panel" id="archives-panel"></div>
|
|
174
|
+
<div class="panel pad" style="margin-top:var(--gap)">
|
|
175
|
+
<p class="note"><b>If this machine is lost:</b> your recovery phrase plus <code>vaultline cloud login</code> on any machine reaches every copy in managed storage. The phrase is the only key — no copy anywhere can be opened without it.</p>
|
|
176
|
+
</div>
|
|
177
|
+
</section>
|
|
178
|
+
|
|
179
|
+
<section id="view-settings" class="view" hidden>
|
|
180
|
+
<div class="metrics">
|
|
181
|
+
<div class="metric"><h3>Waiting to encrypt</h3><b id="set-queue">–</b><small id="set-queue-detail"></small></div>
|
|
182
|
+
<div class="metric"><h3>Last checked in</h3><b id="set-sync" class="small">–</b><small id="set-sync-ago"></small></div>
|
|
183
|
+
<div class="metric"><h3>Sessions preserved</h3><b id="set-archives">–</b><small>on this machine</small></div>
|
|
184
|
+
<div class="metric"><h3>Next reclaim window</h3><b id="set-reclaim-window" class="small">–</b><small id="set-reclaim-detail"></small></div>
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
<form id="settings-form">
|
|
188
|
+
<div class="panel pad">
|
|
189
|
+
<h3>Identity</h3>
|
|
190
|
+
<div class="field">
|
|
191
|
+
<label for="f-device-label">Device label</label>
|
|
192
|
+
<input id="f-device-label" type="text" maxlength="80" autocomplete="off" spellcheck="false" placeholder="this machine's name">
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
<h3>Watching</h3>
|
|
196
|
+
<div class="field">
|
|
197
|
+
<label class="opt"><input id="f-watch-codex" type="checkbox"> Codex</label>
|
|
198
|
+
<label class="opt"><input id="f-watch-claude" type="checkbox"> Claude Code</label>
|
|
199
|
+
</div>
|
|
200
|
+
<div class="field">
|
|
201
|
+
<label for="f-watched-paths">Extra paths to watch, one per line</label>
|
|
202
|
+
<textarea id="f-watched-paths" rows="3" spellcheck="false" placeholder="/mnt/extra-sessions"></textarea>
|
|
203
|
+
</div>
|
|
204
|
+
<div class="field">
|
|
205
|
+
<label for="f-exclusions">Never watch these, one per line</label>
|
|
206
|
+
<textarea id="f-exclusions" rows="3" spellcheck="false" placeholder="/Users/me/client-work/**"></textarea>
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
<h3>Unlocking</h3>
|
|
210
|
+
<div class="field" id="remember-panel">
|
|
211
|
+
<p class="note" id="remember-state">Checking whether this machine holds the recovery phrase…</p>
|
|
212
|
+
<div class="row" id="remember-form" hidden>
|
|
213
|
+
<input id="f-remember-phrase" type="password" autocomplete="off" spellcheck="false" placeholder="your 24-word recovery phrase" aria-label="Recovery phrase">
|
|
214
|
+
<button type="button" class="action" id="remember-go">Remember it on this machine</button>
|
|
215
|
+
</div>
|
|
216
|
+
<p class="note error" id="remember-error" hidden></p>
|
|
217
|
+
<p class="note">Kept in this machine's keychain so archiving can run without you typing the phrase each time. It is checked against the vault before it is stored, and it is never sent anywhere.</p>
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
<h3>Automatic archiving</h3>
|
|
221
|
+
<div class="field" id="autopilot-panel">
|
|
222
|
+
<p class="note" id="autopilot-state">Checking whether the background service is running…</p>
|
|
223
|
+
<div class="row" id="autopilot-form" hidden>
|
|
224
|
+
<button type="button" class="action" id="autopilot-go">Turn on automatic archiving</button>
|
|
225
|
+
</div>
|
|
226
|
+
<p class="note error" id="autopilot-error" hidden></p>
|
|
227
|
+
<p class="note">When on, Sealkeep runs at login and archives new sessions on its own — no terminal, nothing to keep open. Off by default; you can always archive by hand from the <a href="#sessions">Memory pool</a>.</p>
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
<h3>Reclaiming</h3>
|
|
231
|
+
<div class="field">
|
|
232
|
+
<label class="opt"><input id="f-reclaim-enabled" type="checkbox"> Let this machine reclaim disk space on its own</label>
|
|
233
|
+
<p class="note">Off by default. When on, the source moves to the trash only once its archive is verified in storage and the grace period has passed. Policy and timing stay in the <a href="#rules">Free up disk rules</a> — this only switches this machine's permission to act on them.</p>
|
|
234
|
+
</div>
|
|
235
|
+
<div class="field">
|
|
236
|
+
<label for="f-trash-strategy">Trash location</label>
|
|
237
|
+
<select id="f-trash-strategy">
|
|
238
|
+
<option value="auto">Automatic — this machine's own trash</option>
|
|
239
|
+
<option value="macos-trash">macOS Trash</option>
|
|
240
|
+
<option value="xdg-trash">XDG Trash</option>
|
|
241
|
+
<option value="windows-recycle-bin">Windows Recycle Bin</option>
|
|
242
|
+
<option value="vaultline-trash">Sealkeep-owned trash folder</option>
|
|
243
|
+
</select>
|
|
244
|
+
<p class="note" id="f-trash-resolved"></p>
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
<h3>Schedule and bandwidth</h3>
|
|
248
|
+
<div class="row">
|
|
249
|
+
<div class="field"><label for="f-interval">Check every (minutes)</label><input id="f-interval" type="number" min="1" max="1440"></div>
|
|
250
|
+
<div class="field"><label for="f-bandwidth">Upload limit, Mbps (blank for none)</label><input id="f-bandwidth" type="number" min="0" placeholder="unlimited"></div>
|
|
251
|
+
<div class="field">
|
|
252
|
+
<label for="f-reserve">Always keep free, MB (blank for automatic)</label>
|
|
253
|
+
<input id="f-reserve" type="number" min="200" placeholder="automatic">
|
|
254
|
+
<p class="note">Automatic scales with this disk: 2% of it, at least 400 MB, at most 2 GB. On a tight disk you can go as low as 200 MB — below that the OS itself starts failing, so Sealkeep will not.</p>
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
|
|
258
|
+
<div class="row">
|
|
259
|
+
<p class="note" id="settings-saved"></p>
|
|
260
|
+
<div class="actions"><button type="submit" class="action">Save</button></div>
|
|
261
|
+
</div>
|
|
262
|
+
</div>
|
|
263
|
+
</form>
|
|
264
|
+
|
|
265
|
+
<h2>Account</h2>
|
|
266
|
+
<div class="panel pad">
|
|
267
|
+
<p class="note">Set from <a id="set-panel-link" href="https://shared.spala.ai" target="_blank" rel="noopener">the account panel</a>, not here — these apply to every machine on the account, and this machine only mirrors them.</p>
|
|
268
|
+
<div class="metrics">
|
|
269
|
+
<div class="metric"><h3>What leaves this machine</h3><b id="set-sync-mode" class="small">–</b><small id="set-sync-source"></small></div>
|
|
270
|
+
<div class="metric"><h3>Plan</h3><b id="set-plan" class="small">–</b><small> </small></div>
|
|
271
|
+
<div class="metric"><h3>Storage used</h3><b id="set-quota" class="small">–</b><small id="set-quota-detail"></small></div>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
</section>
|
|
275
|
+
|
|
276
|
+
<section id="view-sessions" class="view" hidden>
|
|
277
|
+
<!-- Plan and quota, shown to the person paying for it. A customer on a paid
|
|
278
|
+
tier should be able to see what they are getting somewhere other than an
|
|
279
|
+
invoice. -->
|
|
280
|
+
<div class="planbar" id="planbar" hidden>
|
|
281
|
+
<!-- Plan name, meter and usage live in the rail's identity strip;
|
|
282
|
+
repeating them here said the same thing twice on one screen. What
|
|
283
|
+
stays is the one fact only this view can use well: how much more
|
|
284
|
+
would fit. -->
|
|
285
|
+
<p class="note" id="plan-note"></p>
|
|
286
|
+
</div>
|
|
287
|
+
|
|
288
|
+
<fieldset>
|
|
289
|
+
<legend>What is on disk</legend>
|
|
290
|
+
<p id="summary" class="note">Looking…</p>
|
|
291
|
+
|
|
292
|
+
<!-- 697 rows is not a list, it is a haystack. Filtering is the difference
|
|
293
|
+
between "pick what to archive" being a decision and being a chore. -->
|
|
294
|
+
<!-- One click each, and they combine. "Older than 30 days" plus "over
|
|
295
|
+
1 GB" is two clicks and reads back as a sentence; the same question
|
|
296
|
+
through five dropdowns had to be assembled. Chips in the same group
|
|
297
|
+
(ages, sizes, agents) replace each other; groups stack. -->
|
|
298
|
+
<div class="chips" id="chips" role="group" aria-label="Show only">
|
|
299
|
+
<button type="button" class="chip on" data-group="state" data-value="unarchived">Not archived yet</button>
|
|
300
|
+
<button type="button" class="chip" data-group="state" data-value="sealing">Queued to seal</button>
|
|
301
|
+
<button type="button" class="chip" data-group="state" data-value="archived">Already archived</button>
|
|
302
|
+
<!-- Cloud-state chips are a plan feature: they answer "which of these
|
|
303
|
+
would survive losing this disk", which only means something when the
|
|
304
|
+
account has storage. Unhidden by drawPlan() when a plan exists. -->
|
|
305
|
+
<button type="button" class="chip pro-chip" data-group="state" data-value="stored" hidden title="Sessions with a verified copy in your cloud storage">In your cloud</button>
|
|
306
|
+
<button type="button" class="chip pro-chip" data-group="state" data-value="unstored" hidden title="Sessions that would be lost with this disk — not yet verified in your cloud">Not in cloud yet</button>
|
|
307
|
+
<span class="chip-sep"></span>
|
|
308
|
+
<button type="button" class="chip" data-group="age" data-value="7">Older than 7 days</button>
|
|
309
|
+
<button type="button" class="chip" data-group="age" data-value="30">Older than 30 days</button>
|
|
310
|
+
<button type="button" class="chip" data-group="age" data-value="90">Older than 90 days</button>
|
|
311
|
+
<span class="chip-sep"></span>
|
|
312
|
+
<button type="button" class="chip" data-group="size" data-value="104857600">Over 100 MB</button>
|
|
313
|
+
<button type="button" class="chip" data-group="size" data-value="1073741824">Over 1 GB</button>
|
|
314
|
+
<span class="chip-sep"></span>
|
|
315
|
+
<button type="button" class="chip" data-group="agent" data-value="codex">Codex</button>
|
|
316
|
+
<button type="button" class="chip" data-group="agent" data-value="claude">Claude Code</button>
|
|
317
|
+
</div>
|
|
318
|
+
<div class="filters">
|
|
319
|
+
<input type="search" id="f-search" placeholder="Search paths…" aria-label="Search session paths">
|
|
320
|
+
<select id="f-sort" aria-label="Sort by">
|
|
321
|
+
<option value="size">Biggest first</option>
|
|
322
|
+
<option value="oldest">Oldest first</option>
|
|
323
|
+
<option value="newest">Newest first</option>
|
|
324
|
+
</select>
|
|
325
|
+
<button type="button" id="f-clear" class="linkish">Clear filters</button>
|
|
326
|
+
</div>
|
|
327
|
+
<!-- The count and the action sit together: "13 shown · 21 GB" next to the
|
|
328
|
+
button that selects exactly those 13, so the sentence and the click are
|
|
329
|
+
the same thing. -->
|
|
330
|
+
<div class="shown">
|
|
331
|
+
<span class="note" id="filter-count"></span>
|
|
332
|
+
<button type="button" class="action" id="select-shown">Select all shown</button>
|
|
333
|
+
</div>
|
|
334
|
+
|
|
335
|
+
<div class="scroll">
|
|
336
|
+
<table>
|
|
337
|
+
<thead><tr>
|
|
338
|
+
<th style="width:2rem"><input type="checkbox" id="all" title="Select every unarchived session"></th>
|
|
339
|
+
<th class="n">size</th><th class="n">last touched</th><th>project</th><th>session</th><th>state</th>
|
|
340
|
+
</tr></thead>
|
|
341
|
+
<tbody id="rows"><tr><td colspan="6" class="note">Reading sessions…</td></tr></tbody>
|
|
342
|
+
</table>
|
|
343
|
+
</div>
|
|
344
|
+
<div class="bar">
|
|
345
|
+
<button type="button" id="archive" class="action primary" disabled>Archive selected</button>
|
|
346
|
+
<span id="chosen" class="note"></span>
|
|
347
|
+
</div>
|
|
348
|
+
<p id="result" class="note" style="margin:.8rem 0 0"></p>
|
|
349
|
+
</fieldset>
|
|
350
|
+
|
|
351
|
+
<div class="note">
|
|
352
|
+
<strong>Archiving costs disk before it saves any.</strong> A sealed copy is written next to your vault while the
|
|
353
|
+
original is still there, so free space dips before it rises. On this machine's transcripts the sealed copy is about
|
|
354
|
+
two thirds of the original, so archiving a 4 GB session needs roughly 6 GB free while it works.
|
|
355
|
+
Reclaiming the originals is a separate, explicit step — each row in <a href="#archives">My vault</a> hands it to you once its copy is verified.
|
|
356
|
+
</div>
|
|
357
|
+
</section>
|
|
358
|
+
|
|
359
|
+
<section id="view-rules" class="view" hidden>
|
|
360
|
+
<fieldset>
|
|
361
|
+
<legend>Policy</legend>
|
|
362
|
+
<label class="opt"><input type="radio" name="policy" value="local-only" checked>
|
|
363
|
+
<div><b>Keep everything</b><span>Never reclaim a source file. Safest, and the default.</span></div></label>
|
|
364
|
+
<label class="opt"><input type="radio" name="policy" value="sync-only">
|
|
365
|
+
<div><b>Upload, but keep the original</b><span>Archives go to storage; nothing is removed from disk.</span></div></label>
|
|
366
|
+
<label class="opt"><input type="radio" name="policy" value="archive-and-reclaim">
|
|
367
|
+
<div><b>Reclaim once safely stored</b><span>The source moves to the trash after the archive is verified in storage and the grace period has passed.</span></div></label>
|
|
368
|
+
<label class="opt"><input type="radio" name="policy" value="manual-approval">
|
|
369
|
+
<div><b>Ask me first</b><span>Sealkeep lists what it would reclaim and waits for approval.</span></div></label>
|
|
370
|
+
</fieldset>
|
|
371
|
+
|
|
372
|
+
<fieldset>
|
|
373
|
+
<legend>Timing</legend>
|
|
374
|
+
<div class="rowgrid">
|
|
375
|
+
<div><label class="f" for="older">Keep the source for at least (days)</label><input type="number" id="older" min="0" value="30"></div>
|
|
376
|
+
<div><label class="f" for="grace">Then wait this long after it is verified (days)</label><input type="number" id="grace" min="0" value="7"></div>
|
|
377
|
+
</div>
|
|
378
|
+
<div class="rowgrid" style="margin-top:.75rem">
|
|
379
|
+
<div><label class="f" for="deleteAfter">Delete the archive from storage after (days)</label><input type="number" id="deleteAfter" min="0" placeholder="never"></div>
|
|
380
|
+
<div><label class="f"> </label><span style="font-size:.85rem;color:var(--dim)">Leave empty for never.</span></div>
|
|
381
|
+
</div>
|
|
382
|
+
</fieldset>
|
|
383
|
+
|
|
384
|
+
<fieldset>
|
|
385
|
+
<legend>Which sessions</legend>
|
|
386
|
+
<p style="margin:0 0 .9rem;font-size:.9rem;color:var(--dim)">
|
|
387
|
+
The timings above measure the <em>archive</em> — how long Sealkeep has held a copy. These two measure the
|
|
388
|
+
<em>session</em>: when you last touched it, and whether it is big enough to be worth the trouble.
|
|
389
|
+
</p>
|
|
390
|
+
<div class="rowgrid">
|
|
391
|
+
<div>
|
|
392
|
+
<label class="f" for="idle">Only if untouched for at least (days)</label>
|
|
393
|
+
<input type="number" id="idle" min="0" placeholder="any">
|
|
394
|
+
<span style="font-size:.85rem;color:var(--dim)">Empty means a session's own age is not considered.</span>
|
|
395
|
+
</div>
|
|
396
|
+
<div>
|
|
397
|
+
<label class="f" for="minSize">Only if larger than (MB)</label>
|
|
398
|
+
<input type="number" id="minSize" min="0" placeholder="any">
|
|
399
|
+
<span style="font-size:.85rem;color:var(--dim)">Most sessions are tiny; the space is in a few huge ones.</span>
|
|
400
|
+
</div>
|
|
401
|
+
</div>
|
|
402
|
+
</fieldset>
|
|
403
|
+
|
|
404
|
+
<fieldset>
|
|
405
|
+
<legend>Apply</legend>
|
|
406
|
+
<p id="effect" style="margin:0 0 .9rem">Reading the current rules…</p>
|
|
407
|
+
<div style="display:flex;gap:.6rem;align-items:center;flex-wrap:wrap">
|
|
408
|
+
<button type="button" class="action primary" id="save">Save these rules</button>
|
|
409
|
+
<span id="saved" style="font-size:.85rem;color:var(--dim)"></span>
|
|
410
|
+
</div>
|
|
411
|
+
<p style="margin:1.1rem 0 .35rem"><span class="f">or, if you prefer a terminal</span></p>
|
|
412
|
+
<pre id="out">…</pre>
|
|
413
|
+
</fieldset>
|
|
414
|
+
|
|
415
|
+
<fieldset>
|
|
416
|
+
<legend>Stored copies</legend>
|
|
417
|
+
<p style="margin:0 0 .9rem;font-size:.9rem;color:var(--soft)">
|
|
418
|
+
What your storage is holding right now, and the way to take a copy back out of it. Deleting a stored copy never
|
|
419
|
+
touches the session or the archive on this machine — unless that copy is the last one left anywhere, and then
|
|
420
|
+
this view will stop and say so before anything happens.
|
|
421
|
+
</p>
|
|
422
|
+
<p id="copies-line" style="margin:0 0 .9rem">Looking at what is stored off this machine…</p>
|
|
423
|
+
<div id="copies-wrap">
|
|
424
|
+
<table id="copies-table" hidden>
|
|
425
|
+
<thead><tr><th>Ref</th><th>Size</th><th>Age</th><th></th></tr></thead>
|
|
426
|
+
<tbody id="copies-body"></tbody>
|
|
427
|
+
</table>
|
|
428
|
+
</div>
|
|
429
|
+
|
|
430
|
+
<div id="rolling" hidden style="margin-top:1.2rem;padding-top:1rem;border-top:1px solid var(--rule)">
|
|
431
|
+
<div style="display:flex;gap:.6rem;align-items:center;flex-wrap:wrap">
|
|
432
|
+
<label class="f" for="cap" style="margin:0">Keep only the newest</label>
|
|
433
|
+
<input type="number" id="cap" min="1" placeholder="no cap" style="width:7ch">
|
|
434
|
+
<span style="font-size:.9rem">GB in cloud storage</span>
|
|
435
|
+
<button type="button" class="action" id="cap-save">Save cap</button>
|
|
436
|
+
<span id="cap-msg" style="font-size:.85rem;color:var(--soft)"></span>
|
|
437
|
+
</div>
|
|
438
|
+
<span style="display:block;margin-top:.5rem;font-size:.85rem;color:var(--soft)">
|
|
439
|
+
Your account trims the oldest stored copies past the cap — it applies within a day, not instantly.
|
|
440
|
+
Sessions and archives on this machine are never touched by it. Leave the box empty to keep every copy.
|
|
441
|
+
</span>
|
|
442
|
+
</div>
|
|
443
|
+
</fieldset>
|
|
444
|
+
|
|
445
|
+
<div class="note">
|
|
446
|
+
<strong>Reclaiming is not deleting.</strong> Sources move to the OS trash, never <code>unlink</code>, and only after
|
|
447
|
+
the archive is verified in remote storage, the age and grace periods have both passed, and the file is idle.
|
|
448
|
+
If any of those cannot be established, Sealkeep keeps the file.
|
|
449
|
+
</div>
|
|
450
|
+
|
|
451
|
+
<div class="note" id="gap">
|
|
452
|
+
<strong>Deleting archives:</strong> set how long an archive is kept in storage before it may be deleted, or leave it at
|
|
453
|
+
<em>never</em> — the default, because the surprising outcome for an archive tool is losing an archive, not keeping one.
|
|
454
|
+
Sealkeep reports what has expired; it never deletes an archive on its own.
|
|
455
|
+
</div>
|
|
456
|
+
</section>
|
|
457
|
+
|
|
458
|
+
<section id="view-health" class="view" hidden>
|
|
459
|
+
<div class="panel" id="checks"></div>
|
|
460
|
+
</section>
|
|
461
|
+
</div>
|
|
462
|
+
|
|
463
|
+
<footer>
|
|
464
|
+
<div>
|
|
465
|
+
<b id="footer-title">Encrypted before it leaves</b>
|
|
466
|
+
<span id="footer-note">Archives are sealed on this machine before any provider sees them.</span>
|
|
467
|
+
</div>
|
|
468
|
+
<button id="refresh" class="action" type="button">Refresh</button>
|
|
469
|
+
</footer>
|
|
470
|
+
</main>
|
|
471
|
+
<script src="./rail.js"></script>
|
|
472
|
+
<script src="./app.js"></script>
|
|
473
|
+
<script src="./sessions-view.js"></script>
|
|
474
|
+
<script src="./rules-view.js"></script>
|
|
475
|
+
</body>
|
|
476
|
+
</html>
|