[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled

This commit is contained in:
2026-09-13 18:24:01 +08:00
commit 3c72efcb7f
3255 changed files with 907009 additions and 0 deletions

View File

@@ -0,0 +1,835 @@
"use strict";
const assert = require("node:assert/strict");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const test = require("node:test");
const { DatabaseSync } = require("node:sqlite");
const {
MAX_SECURITY_AUDIT_DETAILS_BYTES,
PluginDatabase,
SCHEMA_VERSION,
} = require("./database.cjs");
function createDatabase(context, clock = () => 1_000) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "netcatty-plugin-db-"));
context.after(() => fs.rmSync(root, { recursive: true, force: true }));
return new PluginDatabase(path.join(root, "plugins.sqlite"), { clock });
}
function manifest(id = "com.example.test", version = "1.0.0") {
return {
manifestVersion: 1,
id,
name: "test",
version,
publisher: "example",
engines: { netcatty: ">=0.0.0", api: ">=0.1.0-internal <0.2.0" },
main: { browser: "dist/index.js" },
};
}
test("plugin database initializes atomically and rejects newer schemas", (context) => {
const database = createDatabase(context);
assert.equal(database.db.prepare("PRAGMA user_version").get().user_version, SCHEMA_VERSION);
assert.equal(database.db.prepare("PRAGMA foreign_keys").get().foreign_keys, 1);
database.close();
const root = fs.mkdtempSync(path.join(os.tmpdir(), "netcatty-plugin-newer-db-"));
context.after(() => fs.rmSync(root, { recursive: true, force: true }));
const file = path.join(root, "plugins.sqlite");
const newer = new DatabaseSync(file);
newer.exec("PRAGMA user_version = 99");
newer.close();
assert.throws(() => new PluginDatabase(file), /newer than supported/);
});
test("obsolete unpublished v1 layouts fail with an explicit reset instruction", (context) => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "netcatty-plugin-obsolete-db-"));
context.after(() => fs.rmSync(root, { recursive: true, force: true }));
const file = path.join(root, "plugins.sqlite");
const obsolete = new DatabaseSync(file);
obsolete.exec("CREATE TABLE plugins(id TEXT PRIMARY KEY); PRAGMA user_version = 1");
obsolete.close();
assert.throws(
() => new PluginDatabase(file),
/reset userData\/plugins\/plugins\.sqlite/,
);
});
test("complete schema-1 databases migrate in place to schema 3 with sidecar and binding tables", (context) => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "netcatty-plugin-v1-migrate-"));
context.after(() => fs.rmSync(root, { recursive: true, force: true }));
const file = path.join(root, "plugins.sqlite");
const v1 = new DatabaseSync(file);
// Full pre-sidecar schema-1 layout (all tables except plugin_sync_sidecars).
v1.exec(`
PRAGMA foreign_keys = ON;
CREATE TABLE plugins (
id TEXT PRIMARY KEY,
enabled INTEGER NOT NULL DEFAULT 0 CHECK (enabled IN (0, 1)),
active_version TEXT,
installed_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE TABLE plugin_versions (
plugin_id TEXT NOT NULL REFERENCES plugins(id) ON DELETE CASCADE,
version TEXT NOT NULL,
manifest_json TEXT NOT NULL,
archive_sha256 TEXT NOT NULL,
package_relative_path TEXT NOT NULL,
installed_at INTEGER NOT NULL,
PRIMARY KEY (plugin_id, version)
);
CREATE TABLE plugin_runtime_state (
plugin_id TEXT NOT NULL,
plugin_version TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'stopped',
runtime_kind TEXT,
last_error TEXT,
quarantined_at INTEGER,
updated_at INTEGER NOT NULL,
PRIMARY KEY (plugin_id, plugin_version),
FOREIGN KEY (plugin_id, plugin_version)
REFERENCES plugin_versions(plugin_id, version) ON DELETE CASCADE
);
CREATE TABLE plugin_crashes (
plugin_id TEXT NOT NULL,
plugin_version TEXT NOT NULL,
crashed_at INTEGER NOT NULL,
FOREIGN KEY (plugin_id, plugin_version)
REFERENCES plugin_versions(plugin_id, version) ON DELETE CASCADE
);
CREATE TABLE plugin_kv (
plugin_id TEXT NOT NULL REFERENCES plugins(id) ON DELETE CASCADE,
key TEXT NOT NULL,
value_json TEXT NOT NULL,
updated_at INTEGER NOT NULL,
PRIMARY KEY (plugin_id, key)
);
CREATE TABLE plugin_settings (
plugin_id TEXT NOT NULL,
setting_id TEXT NOT NULL,
scope TEXT NOT NULL CHECK (scope IN ('application', 'workspace', 'host', 'session', 'device')),
scope_id TEXT NOT NULL,
value_json TEXT NOT NULL,
updated_at INTEGER NOT NULL,
PRIMARY KEY (plugin_id, setting_id, scope, scope_id)
);
CREATE TABLE plugin_view_state (
plugin_id TEXT NOT NULL,
view_id TEXT NOT NULL,
scope_id TEXT NOT NULL,
state_json TEXT NOT NULL,
updated_at INTEGER NOT NULL,
PRIMARY KEY (plugin_id, view_id, scope_id)
);
CREATE TABLE plugin_permission_grants (
plugin_id TEXT NOT NULL,
permission TEXT NOT NULL,
resource TEXT NOT NULL,
resource_kind TEXT NOT NULL CHECK (resource_kind IN ('exact', 'directory')),
declaration_hash TEXT NOT NULL,
granted_at INTEGER NOT NULL,
PRIMARY KEY (plugin_id, permission, resource)
);
CREATE TABLE plugin_secrets (
plugin_id TEXT NOT NULL,
key TEXT NOT NULL,
secret_ref TEXT NOT NULL UNIQUE,
ciphertext BLOB NOT NULL,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
PRIMARY KEY (plugin_id, key)
);
CREATE TABLE plugin_security_audit (
id INTEGER PRIMARY KEY AUTOINCREMENT,
plugin_id TEXT NOT NULL,
event TEXT NOT NULL,
details_json TEXT NOT NULL,
created_at INTEGER NOT NULL
);
PRAGMA user_version = 1;
`);
v1.prepare(
"INSERT INTO plugins(id, enabled, active_version, installed_at, updated_at) VALUES (?, 1, ?, 1, 1)",
).run("com.example.v1", "1.0.0");
v1.close();
const database = new PluginDatabase(file);
assert.equal(database.db.prepare("PRAGMA user_version").get().user_version, SCHEMA_VERSION);
assert.deepEqual(
database.db.prepare("PRAGMA table_info(plugin_sync_sidecars)").all().map(({ name }) => name),
["plugin_id", "kind", "key", "value_json", "updated_at"],
);
assert.deepEqual(
database.db.prepare("PRAGMA table_info(plugin_sync_provider_bindings)").all().map(({ name }) => name),
["provider_id", "plugin_id", "created_at", "updated_at"],
);
// Existing rows survive the in-place migration.
assert.equal(database.db.prepare("SELECT id FROM plugins").get().id, "com.example.v1");
database.setSyncSidecar("com.example.v1", "settings", "theme\0application\0application", "dark", 2);
assert.equal(
database.getSyncSidecar("com.example.v1", "settings", "theme\0application\0application")?.value,
"dark",
);
database.upsertSyncProviderBinding("com.example.v1.sync", "com.example.v1");
assert.equal(database.getSyncProviderBinding("com.example.v1.sync")?.pluginId, "com.example.v1");
database.close();
});
test("initial schema scopes runtime and crash state to immutable plugin versions", (context) => {
const database = createDatabase(context);
assert.deepEqual(
database.db.prepare("PRAGMA table_info(plugin_crashes)").all().map(({ name }) => name),
["plugin_id", "plugin_version", "crashed_at"],
);
assert.deepEqual(
database.db.prepare("PRAGMA table_info(plugin_runtime_state)").all().map(({ name }) => name),
[
"plugin_id",
"plugin_version",
"status",
"runtime_kind",
"last_error",
"quarantined_at",
"updated_at",
],
);
assert.deepEqual(
database.db.prepare("PRAGMA table_info(plugin_permission_grants)").all().map(({ name }) => name),
["plugin_id", "permission", "resource", "resource_kind", "declaration_hash", "granted_at"],
);
assert.deepEqual(
database.db.prepare("PRAGMA table_info(plugin_secrets)").all().map(({ name }) => name),
["plugin_id", "key", "secret_ref", "ciphertext", "created_at", "updated_at"],
);
assert.deepEqual(
database.db.prepare("PRAGMA table_info(plugin_settings)").all().map(({ name }) => name),
["plugin_id", "setting_id", "scope", "scope_id", "value_json", "updated_at"],
);
assert.deepEqual(
database.db.prepare("PRAGMA table_info(plugin_view_state)").all().map(({ name }) => name),
["plugin_id", "view_id", "scope_id", "state_json", "updated_at"],
);
assert.deepEqual(
database.db.prepare("PRAGMA table_info(plugin_sync_sidecars)").all().map(({ name }) => name),
["plugin_id", "kind", "key", "value_json", "updated_at"],
);
assert.deepEqual(
database.db.prepare("PRAGMA table_info(plugin_sync_provider_bindings)").all().map(({ name }) => name),
["provider_id", "plugin_id", "created_at", "updated_at"],
);
assert.deepEqual(database.db.prepare("PRAGMA foreign_key_list(plugin_settings)").all(), []);
assert.deepEqual(database.db.prepare("PRAGMA foreign_key_list(plugin_view_state)").all(), []);
assert.deepEqual(database.db.prepare("PRAGMA foreign_key_list(plugin_sync_sidecars)").all(), []);
database.close();
});
test("user-owned security records survive package uninstall in the complete v1 schema", (context) => {
const database = createDatabase(context);
const pluginManifest = manifest();
database.installVersion({
pluginId: pluginManifest.id,
version: pluginManifest.version,
manifest: pluginManifest,
archiveSha256: "a".repeat(64),
packageRelativePath: `${pluginManifest.id}/${pluginManifest.version}/package`,
});
database.upsertPermissionGrant({
pluginId: pluginManifest.id,
permission: "network",
resource: "https://example.com",
resourceKind: "exact",
declarationHash: "b".repeat(64),
});
database.upsertSecret({
pluginId: pluginManifest.id,
key: "api-key",
secretRef: "secret-reference-0000000000000000",
ciphertext: Buffer.from("encrypted"),
});
database.recordSecurityAudit(pluginManifest.id, "permission.granted", { permission: "network" });
database.removePlugin(pluginManifest.id);
assert.equal(database.getActivePlugin(pluginManifest.id), null);
assert.deepEqual(database.listPermissionGrants(pluginManifest.id).map((grant) => ({
resource: grant.resource,
resourceKind: grant.resourceKind,
})), [{ resource: "https://example.com", resourceKind: "exact" }]);
assert.equal(database.getSecretByKey(pluginManifest.id, "api-key").secretRef, "secret-reference-0000000000000000");
assert.deepEqual(database.listSecurityAudit(pluginManifest.id)[0].details, { permission: "network" });
database.close();
});
test("security audit details are bounded and oversized records retain only a digest", (context) => {
const database = createDatabase(context);
database.recordSecurityAudit("com.example.test", "permission.denied", {
untrusted: "x".repeat(MAX_SECURITY_AUDIT_DETAILS_BYTES + 1),
});
const record = database.listSecurityAudit("com.example.test")[0];
assert.equal(record.details.truncated, true);
assert.match(record.details.sha256, /^[a-f0-9]{64}$/u);
assert.equal(Object.hasOwn(record.details, "untrusted"), false);
database.close();
});
test("version activation and namespaced key/value writes are transactional", (context) => {
const database = createDatabase(context);
const pluginManifest = manifest();
database.installVersion({
pluginId: pluginManifest.id,
version: pluginManifest.version,
manifest: pluginManifest,
archiveSha256: "a".repeat(64),
packageRelativePath: "com.example.test/1.0.0/package",
}, { enable: true });
const installed = database.getActivePlugin(pluginManifest.id);
assert.equal(installed.enabled, true);
assert.equal(installed.activeVersion, "1.0.0");
assert.deepEqual(installed.manifest, pluginManifest);
database.setValue(pluginManifest.id, "greeting", { text: "hello" });
database.setValue(pluginManifest.id, "count", 2);
assert.deepEqual(database.getValue(pluginManifest.id, "greeting"), { text: "hello" });
assert.deepEqual(database.listKeys(pluginManifest.id), ["count", "greeting"]);
database.deleteValue(pluginManifest.id, "count");
assert.equal(database.getValue(pluginManifest.id, "count"), undefined);
database.close();
});
test("database transactions reject async callbacks before committing", (context) => {
const database = createDatabase(context);
assert.throws(() => database.transaction(async () => {
database.db.prepare(`
INSERT INTO plugins(id, enabled, active_version, installed_at, updated_at)
VALUES ('com.example.async', 0, NULL, 1, 1)
`).run();
}), /must be synchronous/);
assert.equal(database.getActivePlugin("com.example.async"), null);
database.close();
});
test("recovered versions can atomically replace an enabled version while staying disabled", (context) => {
const database = createDatabase(context);
const first = manifest();
database.installVersion({
pluginId: first.id,
version: first.version,
manifest: first,
archiveSha256: "a".repeat(64),
packageRelativePath: `${first.id}/${first.version}/package`,
}, { enable: true });
const second = manifest(first.id, "2.0.0");
database.installVersion({
pluginId: second.id,
version: second.version,
manifest: second,
archiveSha256: "b".repeat(64),
packageRelativePath: `${second.id}/${second.version}/package`,
}, { forceDisabled: true });
const recovered = database.getActivePlugin(first.id);
assert.equal(recovered.activeVersion, "2.0.0");
assert.equal(recovered.enabled, false);
assert.throws(() => database.installVersion({
pluginId: second.id,
version: second.version,
manifest: second,
archiveSha256: "b".repeat(64),
packageRelativePath: `${second.id}/${second.version}/package`,
}, { enable: true, forceDisabled: true }), /cannot be enabled and force-disabled/);
database.close();
});
test("active version rollback is compare-and-set and keeps version state isolated", (context) => {
const database = createDatabase(context);
const first = manifest();
const second = manifest(first.id, "2.0.0");
for (const [pluginManifest, archive] of [[first, "a"], [second, "b"]]) {
database.installVersion({
pluginId: pluginManifest.id,
version: pluginManifest.version,
manifest: pluginManifest,
archiveSha256: archive.repeat(64),
packageRelativePath: `${pluginManifest.id}/${pluginManifest.version}/package`,
}, { enable: true });
}
database.setRuntimeState(first.id, "error", {
pluginVersion: second.version,
error: "new version failed",
});
const restored = database.setActiveVersion(first.id, first.version, {
enabled: true,
expectedActiveVersion: second.version,
});
assert.equal(restored.activeVersion, first.version);
assert.equal(restored.enabled, true);
assert.equal(restored.runtime.status, "stopped");
assert.equal(database.getVersion(first.id, second.version).version, second.version);
assert.throws(() => database.setActiveVersion(first.id, second.version, {
enabled: true,
expectedActiveVersion: "3.0.0",
}), /changed before it could be restored/);
assert.throws(() => database.setActiveVersion(first.id, "9.0.0"), /version is not installed/);
database.close();
});
test("three crashes inside five minutes quarantine until explicit recovery", (context) => {
let now = 10_000;
const database = createDatabase(context, () => now);
const pluginManifest = manifest();
database.installVersion({
pluginId: pluginManifest.id,
version: pluginManifest.version,
manifest: pluginManifest,
archiveSha256: "a".repeat(64),
packageRelativePath: "com.example.test/1.0.0/package",
});
assert.deepEqual(database.recordCrash(pluginManifest.id, pluginManifest.version, 300_000, 3), {
count: 1, quarantined: false, quarantinedAt: null,
});
now += 1_000;
assert.equal(database.recordCrash(pluginManifest.id, pluginManifest.version, 300_000, 3).quarantined, false);
now += 1_000;
assert.equal(database.recordCrash(pluginManifest.id, pluginManifest.version, 300_000, 3).quarantined, true);
assert.equal(database.getActivePlugin(pluginManifest.id).runtime.status, "quarantined");
database.clearQuarantine(pluginManifest.id);
assert.equal(database.getActivePlugin(pluginManifest.id).runtime.quarantinedAt, null);
assert.equal(database.getActivePlugin(pluginManifest.id).runtime.status, "stopped");
database.close();
});
test("activating a new version resets runtime quarantine without forgiving the same version", (context) => {
const database = createDatabase(context);
const first = manifest();
database.installVersion({
pluginId: first.id,
version: first.version,
manifest: first,
archiveSha256: "a".repeat(64),
packageRelativePath: `${first.id}/${first.version}/package`,
}, { enable: true });
for (let attempt = 0; attempt < 3; attempt += 1) {
database.recordCrash(first.id, first.version, 300_000, 3);
}
assert.equal(database.getActivePlugin(first.id).runtime.status, "quarantined");
database.installVersion({
pluginId: first.id,
version: first.version,
manifest: first,
archiveSha256: "a".repeat(64),
packageRelativePath: `${first.id}/${first.version}/package`,
});
assert.equal(database.getActivePlugin(first.id).runtime.status, "quarantined");
const second = manifest(first.id, "2.0.0");
database.installVersion({
pluginId: second.id,
version: second.version,
manifest: second,
archiveSha256: "b".repeat(64),
packageRelativePath: `${second.id}/${second.version}/package`,
});
const active = database.getActivePlugin(first.id);
assert.equal(active.activeVersion, "2.0.0");
assert.equal(active.runtime.status, "stopped");
assert.equal(active.runtime.lastError, null);
assert.equal(active.runtime.quarantinedAt, null);
assert.deepEqual(database.recordCrash(second.id, second.version, 300_000, 3), {
count: 1,
quarantined: false,
quarantinedAt: null,
});
assert.deepEqual(database.recordCrash(first.id, first.version, 300_000, 3), {
count: 4,
quarantined: true,
quarantinedAt: 1_000,
});
database.clearQuarantine(second.id);
assert.equal(Number(database.db.prepare(`
SELECT COUNT(*) AS count FROM plugin_crashes
WHERE plugin_id = ? AND plugin_version = ?
`).get(first.id, first.version).count), 4);
assert.equal(Number(database.db.prepare(`
SELECT COUNT(*) AS count FROM plugin_crashes
WHERE plugin_id = ? AND plugin_version = ?
`).get(second.id, second.version).count), 0);
database.installVersion({
pluginId: first.id,
version: first.version,
manifest: first,
archiveSha256: "a".repeat(64),
packageRelativePath: `${first.id}/${first.version}/package`,
});
assert.equal(database.getActivePlugin(first.id).runtime.status, "quarantined");
assert.equal(database.getActivePlugin(first.id).runtime.quarantinedAt, 1_000);
database.close();
});
test("schema upgrade backfills bindings from legacy sync-provider-map secrets", (context) => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "netcatty-plugin-v2-backfill-"));
context.after(() => fs.rmSync(root, { recursive: true, force: true }));
const file = path.join(root, "plugins.sqlite");
// Bootstrap a schema-2 DB then open with PluginDatabase to migrate to v3.
const seed = new PluginDatabase(file);
// Force downgrade-like state: empty bindings + a legacy map secret row.
seed.db.exec("DELETE FROM plugin_sync_provider_bindings");
const now = Date.now();
seed.db.prepare(`
INSERT INTO plugin_secrets(plugin_id, key, secret_ref, ciphertext, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
`).run(
"com.example",
"sync-provider-map:com.example.sync",
"ref-legacy-map",
Buffer.from("sealed"),
now,
now,
);
seed.db.prepare(`
INSERT INTO plugin_secrets(plugin_id, key, secret_ref, ciphertext, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
`).run(
"com.example",
"sync-credential",
"ref-cred",
Buffer.from("sealed-cred"),
now,
now,
);
seed.close();
const database = new PluginDatabase(file);
// Constructor no longer promotes; hostService seeds after package recovery.
assert.equal(database.backfillSyncProviderBindingsFromLegacySecrets(), 1);
assert.equal(database.getSyncProviderBinding("com.example.sync")?.pluginId, "com.example");
// Consumed map marker so later unbind + reopen cannot re-promote.
assert.equal(
database.getSecretByKey("com.example", "sync-provider-map:com.example.sync"),
null,
"promoted map markers must be deleted",
);
assert.ok(database.getSecretByKey("com.example", "sync-credential"));
database.close();
});
test("legacy map backfill does not overwrite an existing host binding", (context) => {
const database = createDatabase(context);
const now = Date.now();
// Correct owner already bound.
database.upsertSyncProviderBinding("com.example.sync.foo", "com.example.sync");
// Stale parent leftover map secret that would steal ownership if upserted.
database.db.prepare(`
INSERT INTO plugin_secrets(plugin_id, key, secret_ref, ciphertext, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
`).run(
"com.example",
"sync-provider-map:com.example.sync.foo",
"ref-stale-parent-map",
Buffer.from("sealed"),
now,
now,
);
const promoted = database.backfillSyncProviderBindingsFromLegacySecrets();
assert.equal(promoted, 0, "must not promote over an existing binding");
assert.equal(
database.getSyncProviderBinding("com.example.sync.foo")?.pluginId,
"com.example.sync",
"existing binding must be preserved",
);
// Skipped promote leaves non-winning map rows in place (only winners are consumed).
assert.ok(
database.getSecretByKey("com.example", "sync-provider-map:com.example.sync.foo"),
"non-promoted leftover map rows stay until unbind consumes them",
);
database.close();
});
test("legacy map backfill skips when no candidate holds sync credentials", (context) => {
const database = createDatabase(context);
const now = Date.now();
// Map-only leftovers: longest namespace must not invent ownership without
// sync-credential* evidence (parent may be the true owner later).
for (const [pluginId, key] of [
["com.example", "sync-provider-map:com.example.sync.foo"],
["com.example.sync", "sync-provider-map:com.example.sync.foo"],
]) {
database.db.prepare(`
INSERT INTO plugin_secrets(plugin_id, key, secret_ref, ciphertext, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
`).run(pluginId, key, `ref-${pluginId}`, Buffer.from("sealed"), now, now);
}
const promoted = database.backfillSyncProviderBindingsFromLegacySecrets();
assert.equal(promoted, 0, "map-only candidates must not bind without credentials");
assert.equal(database.getSyncProviderBinding("com.example.sync.foo"), null);
// Unbound maps stay so a later credential-backed promote or live put can resolve.
assert.ok(database.getSecretByKey("com.example", "sync-provider-map:com.example.sync.foo"));
assert.ok(database.getSecretByKey("com.example.sync", "sync-provider-map:com.example.sync.foo"));
database.close();
});
test("legacy map backfill binds sole credential-backed owner when maps conflict", (context) => {
const database = createDatabase(context);
const now = Date.now();
// Parent has leftover map only; nested owner still holds credentials.
for (const [pluginId, key, ciphertext] of [
["com.example", "sync-provider-map:com.example.sync.foo", "map-parent"],
["com.example.sync", "sync-provider-map:com.example.sync.foo", "map-nested"],
["com.example.sync", "sync-credential", "real-secret"],
]) {
database.db.prepare(`
INSERT INTO plugin_secrets(plugin_id, key, secret_ref, ciphertext, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
`).run(pluginId, key, `ref-${pluginId}-${key}`, Buffer.from(ciphertext), now, now);
}
const promoted = database.backfillSyncProviderBindingsFromLegacySecrets();
assert.equal(promoted, 1);
assert.equal(
database.getSyncProviderBinding("com.example.sync.foo")?.pluginId,
"com.example.sync",
"sole credential-backed owner must win over map-only parent",
);
// All candidate map markers for the promoted provider are consumed.
assert.equal(database.getSecretByKey("com.example", "sync-provider-map:com.example.sync.foo"), null);
assert.equal(database.getSecretByKey("com.example.sync", "sync-provider-map:com.example.sync.foo"), null);
assert.ok(database.getSecretByKey("com.example.sync", "sync-credential"));
database.close();
});
test("legacy map backfill does not let longest map override credential-backed parent", (context) => {
const database = createDatabase(context);
const now = Date.now();
// True owner is the shorter parent (still has credentials). Nested plugin has
// only a stale map row - longest map alone must not steal the binding.
for (const [pluginId, key, ciphertext] of [
["com.example", "sync-provider-map:com.example.sync.foo", "map-parent"],
["com.example", "sync-credential", "parent-secret"],
["com.example.sync", "sync-provider-map:com.example.sync.foo", "map-nested"],
]) {
database.db.prepare(`
INSERT INTO plugin_secrets(plugin_id, key, secret_ref, ciphertext, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
`).run(pluginId, key, `ref-${pluginId}-${key}`, Buffer.from(ciphertext), now, now);
}
const promoted = database.backfillSyncProviderBindingsFromLegacySecrets();
assert.equal(promoted, 1);
assert.equal(
database.getSyncProviderBinding("com.example.sync.foo")?.pluginId,
"com.example",
"credential-backed parent must win over longer map-only nested id",
);
assert.equal(database.getSecretByKey("com.example", "sync-provider-map:com.example.sync.foo"), null);
assert.equal(database.getSecretByKey("com.example.sync", "sync-provider-map:com.example.sync.foo"), null);
assert.ok(database.getSecretByKey("com.example", "sync-credential"));
database.close();
});
test("legacy map backfill skips when multiple credential-backed candidates exist", (context) => {
const database = createDatabase(context);
const now = Date.now();
// Both parent and nested hold credentials + map for the same provider.
// Longest pluginId is not a reliable owner signal — the shorter parent may
// be the legitimate owner — so leave unbound rather than guessing.
for (const [pluginId, key, ciphertext] of [
["com.example", "sync-provider-map:com.example.sync.foo", "map-parent"],
["com.example", "sync-credential", "parent-secret"],
["com.example.sync", "sync-provider-map:com.example.sync.foo", "map-nested"],
["com.example.sync", "sync-credential", "nested-secret"],
]) {
database.db.prepare(`
INSERT INTO plugin_secrets(plugin_id, key, secret_ref, ciphertext, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
`).run(pluginId, key, `ref-${pluginId}-${key}`, Buffer.from(ciphertext), now, now);
}
const promoted = database.backfillSyncProviderBindingsFromLegacySecrets();
assert.equal(promoted, 0, "ambiguous credential-backed candidates must not auto-bind");
assert.equal(database.getSyncProviderBinding("com.example.sync.foo"), null);
// Maps stay so a live put / explicit bind can resolve ownership later.
assert.ok(database.getSecretByKey("com.example", "sync-provider-map:com.example.sync.foo"));
assert.ok(database.getSecretByKey("com.example.sync", "sync-provider-map:com.example.sync.foo"));
assert.ok(database.getSecretByKey("com.example", "sync-credential"));
assert.ok(database.getSecretByKey("com.example.sync", "sync-credential"));
database.close();
});
test("legacy map backfill still consumes maps after unbind tombstone blocks promote", (context) => {
// Verify prior fix: explicit unbind tombstone blocks re-promote; maps may remain
// until unbind/delete paths consume them (backfill itself skips and does not
// delete when promote is blocked).
const database = createDatabase(context);
const now = Date.now();
database.upsertSyncProviderBinding("com.example.sync.foo", "");
database.db.prepare(`
INSERT INTO plugin_secrets(plugin_id, key, secret_ref, ciphertext, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
`).run(
"com.example",
"sync-provider-map:com.example.sync.foo",
"ref-tombstone-map",
Buffer.from("sealed"),
now,
now,
);
database.db.prepare(`
INSERT INTO plugin_secrets(plugin_id, key, secret_ref, ciphertext, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
`).run(
"com.example",
"sync-credential",
"ref-cred",
Buffer.from("secret"),
now,
now,
);
assert.equal(database.backfillSyncProviderBindingsFromLegacySecrets(), 0);
assert.equal(
database.getSyncProviderBinding("com.example.sync.foo")?.pluginId,
"",
"unbind tombstone must block credential-backed map promote",
);
// Map row stays; disconnect/unbind consumption is responsible for cleanup.
assert.ok(
database.getSecretByKey("com.example", "sync-provider-map:com.example.sync.foo"),
"skipped promote leaves map markers for unbind to consume",
);
database.close();
});
test("legacy map backfill deletes promoted map markers so unbind cannot resurrect", (context) => {
const database = createDatabase(context);
const now = Date.now();
database.db.prepare(`
INSERT INTO plugin_secrets(plugin_id, key, secret_ref, ciphertext, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
`).run(
"com.example",
"sync-provider-map:com.example.custom",
"ref-plugin-owned",
Buffer.from("plugin-owned-payload"),
now,
now,
);
// Credential evidence required to promote; non-map secrets must survive map consume.
database.db.prepare(`
INSERT INTO plugin_secrets(plugin_id, key, secret_ref, ciphertext, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
`).run(
"com.example",
"sync-credential",
"ref-cred-keep",
Buffer.from("cred"),
now,
now,
);
assert.equal(database.backfillSyncProviderBindingsFromLegacySecrets(), 1);
assert.equal(database.getSyncProviderBinding("com.example.custom")?.pluginId, "com.example");
assert.equal(
database.getSecretByKey("com.example", "sync-provider-map:com.example.custom"),
null,
"promoted map markers must be deleted to stop post-unbind resurrection",
);
assert.ok(database.getSecretByKey("com.example", "sync-credential"));
database.close();
});
test("legacy map backfill does not resurrect after explicit unbind tombstone", (context) => {
const database = createDatabase(context);
const now = Date.now();
// Pre-seed a leftover map after an explicit empty-plugin_id unbind tombstone.
database.upsertSyncProviderBinding("com.example.sync", "");
database.db.prepare(`
INSERT INTO plugin_secrets(plugin_id, key, secret_ref, ciphertext, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
`).run(
"com.example",
"sync-provider-map:com.example.sync",
"ref-leftover-map",
Buffer.from("sealed"),
now,
now,
);
assert.equal(database.backfillSyncProviderBindingsFromLegacySecrets(), 0);
assert.equal(
database.getSyncProviderBinding("com.example.sync")?.pluginId,
"",
"empty-plugin_id unbind tombstone must block re-promotion",
);
database.close();
});
test("listInstalledVersions includes inactive package manifests", (context) => {
const database = createDatabase(context);
const now = Date.now();
// Install two versions under one plugin id via raw rows if helpers are heavy.
database.db.prepare(`
INSERT INTO plugins(id, enabled, active_version, installed_at, updated_at)
VALUES (?, 1, ?, ?, ?)
`).run("com.example", "2.0.0", now, now);
for (const [version, providerId] of [
["1.0.0", "com.example.legacy-sync"],
["2.0.0", "com.example.sync"],
]) {
database.db.prepare(`
INSERT INTO plugin_versions(
plugin_id, version, manifest_json, archive_sha256, package_relative_path, installed_at
) VALUES (?, ?, ?, ?, ?, ?)
`).run(
"com.example",
version,
JSON.stringify({
id: "com.example",
version,
contributes: { providers: [{ id: providerId, kind: "sync", label: providerId }] },
}),
"a".repeat(64),
`com.example/${version}/package`,
now,
);
}
const versions = database.listInstalledVersions();
assert.equal(versions.length, 2);
const providerIds = new Set();
for (const v of versions) {
for (const p of v.manifest?.contributes?.providers ?? []) {
if (p.kind === "sync") providerIds.add(p.id);
}
}
assert.ok(providerIds.has("com.example.legacy-sync"));
assert.ok(providerIds.has("com.example.sync"));
// Active list only has 2.0.0
assert.equal(database.listPlugins()[0]?.activeVersion, "2.0.0");
database.close();
});
test("inferPluginIdForSyncProvider never guesses from credential key prefixes", (context) => {
const database = createDatabase(context);
const now = Date.now();
for (const [pluginId, key] of [
["com.example", "sync-credential"],
["com.example.backup", "sync-credential"],
["com.other", "sync-credential"],
["com.example.sync", "sync-credential"],
]) {
database.db.prepare(`
INSERT INTO plugin_secrets(plugin_id, key, secret_ref, ciphertext, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
`).run(pluginId, key, `ref-${pluginId}`, Buffer.from("x"), now, now);
}
// Parent prefix alone is not enough: a removed plugin may have owned
// com.example.backup.sync while only com.example still has credentials.
assert.equal(database.inferPluginIdForSyncProvider("com.example.backup.sync"), undefined);
assert.equal(database.inferPluginIdForSyncProvider("com.example.sync"), undefined);
assert.equal(database.inferPluginIdForSyncProvider("com.other.cloud"), undefined);
assert.equal(database.inferPluginIdForSyncProvider("com.missing.sync"), undefined);
database.close();
});