sealcode 1.4.0 → 1.4.1
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/README.md +4 -2
- package/package.json +2 -2
- package/src/cli.js +28 -6
- package/src/hooks.js +15 -3
- package/src/status.js +34 -3
package/README.md
CHANGED
|
@@ -84,12 +84,14 @@ sealcode lock # encrypt source → vendor/ blobs (removes plaintext)
|
|
|
84
84
|
sealcode unlock # decrypt blobs → restore source (removes stubs)
|
|
85
85
|
sealcode verify # confirm every blob decrypts & matches its hash
|
|
86
86
|
sealcode status # show locked/unlocked + drift since last lock
|
|
87
|
-
sealcode status --check # exit 1 if unlocked
|
|
87
|
+
sealcode status --check # exit 1 if unlocked (used by git hook). 1.4.1+: strict — passes only when locked.
|
|
88
|
+
sealcode status --check --allow-clean-unlock # 1.4.1+ escape hatch: only fail on drift
|
|
88
89
|
sealcode status --json # machine-readable state (editors / scripts)
|
|
89
90
|
sealcode rotate # change passphrase (blobs unchanged); env: SEALCODE_OLD_PASSPHRASE / SEALCODE_NEW_PASSPHRASE
|
|
90
91
|
sealcode backup <dir> # copy locked vault + config snapshot to a new folder
|
|
91
92
|
sealcode restore <dir> # restore from backup (use --force if locked dir exists)
|
|
92
|
-
sealcode install-hook # git pre-commit: block
|
|
93
|
+
sealcode install-hook # git pre-commit: block any commit while the project is unlocked (strict by default in 1.4.1+)
|
|
94
|
+
sealcode install-hook --lenient # pre-1.4.1 behavior: only block when the working tree has drifted vs the lock
|
|
93
95
|
sealcode uninstall-hook # remove hook block
|
|
94
96
|
sealcode panic # immediate re-lock + session wipe
|
|
95
97
|
sealcode logout # clear cached session, force passphrase next time
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sealcode",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Lock your source code in your own git repo. Stop AI agents, scrapers, and curious eyes from reading what's yours.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"encryption",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"repository": {
|
|
26
26
|
"type": "git",
|
|
27
|
-
"url": "https://github.com/sealcode/sealcode.git"
|
|
27
|
+
"url": "git+https://github.com/sealcode/sealcode.git"
|
|
28
28
|
},
|
|
29
29
|
"license": "MIT",
|
|
30
30
|
"author": "sealcode",
|
package/src/cli.js
CHANGED
|
@@ -465,14 +465,25 @@ function build() {
|
|
|
465
465
|
program
|
|
466
466
|
.command('status')
|
|
467
467
|
.description('Show whether the project is locked / unlocked and any drift.')
|
|
468
|
-
.option('--check', 'exit 1 if unlocked
|
|
468
|
+
.option('--check', 'exit 1 if unlocked (for git hooks). sealcode@1.4.1: strict by default — passes only when locked.', false)
|
|
469
|
+
// sealcode@1.4.1 — opt back into the pre-1.4.1 "block only on
|
|
470
|
+
// drift" behavior. Useful for CI jobs that intentionally commit
|
|
471
|
+
// alongside an unlocked working tree (rare). The installed hook
|
|
472
|
+
// can also flip into this mode globally via
|
|
473
|
+
// `sealcode install-hook --lenient`.
|
|
474
|
+
.option('--allow-clean-unlock', 'pre-1.4.1 behavior: pass when unlocked as long as there is no drift', false)
|
|
469
475
|
.option('--json', 'machine-readable output (for editors / CI)', false)
|
|
470
476
|
.action(async (opts) => {
|
|
471
477
|
try {
|
|
472
478
|
const projectRoot = resolveProject(program.opts());
|
|
473
479
|
const config = getActiveConfig(projectRoot);
|
|
474
480
|
if (opts.check) {
|
|
475
|
-
const r = await runPrecommitCheck(
|
|
481
|
+
const r = await runPrecommitCheck(
|
|
482
|
+
projectRoot,
|
|
483
|
+
config,
|
|
484
|
+
() => getKeyForCheck(projectRoot, config),
|
|
485
|
+
{ allowCleanUnlock: !!opts.allowCleanUnlock },
|
|
486
|
+
);
|
|
476
487
|
if (!r.ok) {
|
|
477
488
|
process.stderr.write(r.message + '\n');
|
|
478
489
|
process.exitCode = 1;
|
|
@@ -730,12 +741,23 @@ function build() {
|
|
|
730
741
|
// -------- install-hook --------
|
|
731
742
|
program
|
|
732
743
|
.command('install-hook')
|
|
733
|
-
.description('Install a git pre-commit hook that runs `sealcode status --check
|
|
734
|
-
.
|
|
744
|
+
.description('Install a git pre-commit hook that runs `sealcode status --check`.')
|
|
745
|
+
// sealcode@1.4.1 — the hook is strict by default (blocks any
|
|
746
|
+
// commit while the project is unlocked). `--lenient` writes a
|
|
747
|
+
// hook that uses `--allow-clean-unlock` instead, restoring the
|
|
748
|
+
// pre-1.4.1 "block only on drift" behavior.
|
|
749
|
+
.option('--lenient', 'install the pre-1.4.1 hook (only blocks commits when working tree drifts)', false)
|
|
750
|
+
.action((opts) => {
|
|
735
751
|
try {
|
|
736
752
|
const projectRoot = resolveProject(program.opts());
|
|
737
|
-
const hookPath = installHook(projectRoot);
|
|
738
|
-
process.stdout.write(`✓ pre-commit hook installed:\n ${hookPath}\n`);
|
|
753
|
+
const hookPath = installHook(projectRoot, { lenient: !!opts.lenient });
|
|
754
|
+
process.stdout.write(`✓ pre-commit hook installed (${opts.lenient ? 'lenient' : 'strict'} mode):\n ${hookPath}\n`);
|
|
755
|
+
if (!opts.lenient) {
|
|
756
|
+
process.stdout.write(
|
|
757
|
+
' Any `git commit` while the project is unlocked will be blocked.\n' +
|
|
758
|
+
' Run `sealcode lock` before committing.\n',
|
|
759
|
+
);
|
|
760
|
+
}
|
|
739
761
|
} catch (err) {
|
|
740
762
|
process.exitCode = reportError(err);
|
|
741
763
|
}
|
package/src/hooks.js
CHANGED
|
@@ -41,8 +41,18 @@ function findGitDir(startDir) {
|
|
|
41
41
|
/**
|
|
42
42
|
* Install the pre-commit hook. Idempotent: replaces only the sealcode block
|
|
43
43
|
* (and any legacy vaultline block left over from an older install).
|
|
44
|
+
*
|
|
45
|
+
* sealcode@1.4.1 — `opts.lenient` toggles the behavior of the hook
|
|
46
|
+
* itself, not just the install message. Strict (default) refuses any
|
|
47
|
+
* commit while the project is unlocked. Lenient appends
|
|
48
|
+
* `--allow-clean-unlock`, restoring the pre-1.4.1 "block only on drift"
|
|
49
|
+
* behavior for niche workflows that intentionally commit alongside an
|
|
50
|
+
* unlocked tree.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} projectRoot
|
|
53
|
+
* @param {{ lenient?: boolean }} [opts]
|
|
44
54
|
*/
|
|
45
|
-
function installHook(projectRoot) {
|
|
55
|
+
function installHook(projectRoot, opts = {}) {
|
|
46
56
|
const gitDir = findGitDir(projectRoot);
|
|
47
57
|
if (!gitDir) {
|
|
48
58
|
throw new Error('No .git directory found above this folder. Run `git init` first.');
|
|
@@ -50,15 +60,17 @@ function installHook(projectRoot) {
|
|
|
50
60
|
const hookPath = path.join(gitDir, 'hooks', 'pre-commit');
|
|
51
61
|
ensureHooksDir(gitDir);
|
|
52
62
|
|
|
63
|
+
const flag = opts.lenient ? ' --allow-clean-unlock' : '';
|
|
53
64
|
const block = [
|
|
54
65
|
MARK_BEGIN,
|
|
66
|
+
`# mode: ${opts.lenient ? 'lenient' : 'strict'}`,
|
|
55
67
|
'sealcode_check() {',
|
|
56
68
|
' ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || return 0',
|
|
57
69
|
' cd "$ROOT" || return 0',
|
|
58
70
|
' if command -v sealcode >/dev/null 2>&1; then',
|
|
59
|
-
|
|
71
|
+
` sealcode status --check${flag} || exit 1`,
|
|
60
72
|
' elif command -v npx >/dev/null 2>&1; then',
|
|
61
|
-
|
|
73
|
+
` npx --yes sealcode@latest status --check${flag} || exit 1`,
|
|
62
74
|
' else',
|
|
63
75
|
' echo "sealcode: install the CLI (npm i -g sealcode) or npx for pre-commit checks." >&2',
|
|
64
76
|
' exit 1',
|
package/src/status.js
CHANGED
|
@@ -155,11 +155,27 @@ function renderStatus(status) {
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
/**
|
|
158
|
-
* Pre-commit / CI gate
|
|
159
|
-
*
|
|
158
|
+
* Pre-commit / CI gate.
|
|
159
|
+
*
|
|
160
|
+
* sealcode@1.4.1 — strict by default. Any commit while the project is
|
|
161
|
+
* in the `unlocked` state is rejected, regardless of whether the working
|
|
162
|
+
* tree has drifted vs. the last lock. Before 1.4.1 we only blocked when
|
|
163
|
+
* drift was non-zero, which let a recipient `sealcode unlock && git add
|
|
164
|
+
* . && git commit` push the entire decrypted source into git without a
|
|
165
|
+
* single warning. The new default closes that footgun.
|
|
166
|
+
*
|
|
167
|
+
* Callers (CI scripts, IDE integrations, niche workflows) can opt back
|
|
168
|
+
* into the old "block only on drift" behavior with
|
|
169
|
+
* `{ allowCleanUnlock: true }`. The flag is also surfaced as a CLI
|
|
170
|
+
* option on `sealcode status --check`.
|
|
171
|
+
*
|
|
172
|
+
* @param {string} projectRoot
|
|
173
|
+
* @param {object} config
|
|
160
174
|
* @param {( ) => Promise<Buffer|null>} getK
|
|
175
|
+
* @param {{ allowCleanUnlock?: boolean }} [opts]
|
|
161
176
|
*/
|
|
162
|
-
async function runPrecommitCheck(projectRoot, config, getK) {
|
|
177
|
+
async function runPrecommitCheck(projectRoot, config, getK, opts = {}) {
|
|
178
|
+
const allowCleanUnlock = !!opts.allowCleanUnlock;
|
|
163
179
|
const s = await runStatus({ projectRoot, config });
|
|
164
180
|
if (!s.initialized) return { ok: true, message: '' };
|
|
165
181
|
if (s.state === 'locked') return { ok: true, message: '' };
|
|
@@ -171,6 +187,21 @@ async function runPrecommitCheck(projectRoot, config, getK) {
|
|
|
171
187
|
};
|
|
172
188
|
}
|
|
173
189
|
|
|
190
|
+
// Strict default: refuse to let an unlocked tree reach `git commit`.
|
|
191
|
+
// We do this BEFORE computing drift because computing drift requires
|
|
192
|
+
// K, and we don't want a failed `getK()` to mask a much more
|
|
193
|
+
// important "you are about to commit plaintext source" warning.
|
|
194
|
+
if (!allowCleanUnlock) {
|
|
195
|
+
return {
|
|
196
|
+
ok: false,
|
|
197
|
+
message:
|
|
198
|
+
'sealcode: project is UNLOCKED. Committing now would put plaintext source into git.\n' +
|
|
199
|
+
' Fix: run `sealcode lock` first, then `git add` the updated locked blobs.\n' +
|
|
200
|
+
' (Override only if you really mean it: `sealcode status --check --allow-clean-unlock`,\n' +
|
|
201
|
+
' or re-install the hook with `sealcode install-hook --lenient`.)',
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
174
205
|
const K = await getK();
|
|
175
206
|
if (!K) {
|
|
176
207
|
return {
|