Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
de3b919
test(plugin): define durable installation store contract
seonghobae Aug 10, 2026
ef6a033
test(plugin): define installation persistence migration
seonghobae Aug 10, 2026
8426598
feat(plugin): add installation authority migration
seonghobae Aug 10, 2026
3484b96
feat(plugin): persist installation authority atomically
seonghobae Aug 10, 2026
886a148
test(plugin): preserve durable timestamp on idempotent replay
seonghobae Aug 10, 2026
8c9b7d5
fix(plugin): keep original timestamp on installation replay
seonghobae Aug 10, 2026
89401ed
docs(plugin): trace durable installation persistence
seonghobae Aug 10, 2026
8032f80
test(plugin): require workspace-scoped installation lookup
seonghobae Aug 10, 2026
c3e19aa
fix(plugin): keep installation lookup tenant-scoped in persistence
seonghobae Aug 10, 2026
6446b21
test(plugin): require workspace predicate for installation lookup
seonghobae Aug 10, 2026
35b6ac7
fix(plugin): scope persisted installation reads by workspace
seonghobae Aug 10, 2026
2e57e3f
test(plugin): require installer user authority for lookup and revoke
seonghobae Aug 10, 2026
c2a1c4e
Merge branch 'main' into feat/plugin-installation-persistence
github-actions[bot] Aug 10, 2026
d0e9aee
fix(plugin): bind installation authority to installer user
seonghobae Aug 10, 2026
acc2151
fix(plugin): bind durable installation authority to installer
seonghobae Aug 10, 2026
2cd0344
test(plugin): prove installer-scoped persistence outcomes
seonghobae Aug 10, 2026
06c9063
test(plugin): prove tenant and installer lookup isolation
seonghobae Aug 10, 2026
d4bcafe
test(plugin): exercise installation constraints in PostgreSQL
seonghobae Aug 10, 2026
5d0ada8
docs(plugin): align persistence evidence with current standards
seonghobae Aug 10, 2026
3a9ab93
fix(plugin): use CommonJS-safe migration path
seonghobae Aug 10, 2026
1cf609c
test(plugin): reject invalid persisted capability elements
seonghobae Aug 10, 2026
8089069
fix(plugin): constrain persisted capability elements
seonghobae Aug 10, 2026
5356260
Merge branch 'main' into feat/plugin-installation-persistence
github-actions[bot] Aug 10, 2026
90ae9c1
docs(plugin): record durable installation authority
seonghobae Aug 10, 2026
674a49a
test(plugin): enforce durable installation constraints
seonghobae Aug 10, 2026
ab01e99
fix(plugin): enforce durable installation invariants
seonghobae Aug 10, 2026
b45f692
test(plugin): isolate durable installation evidence
seonghobae Aug 10, 2026
a952044
ci(plugin): expose dedicated integration database
seonghobae Aug 10, 2026
7206b09
ci(plugin): run dedicated integration persistence tests
seonghobae Aug 10, 2026
55c42f9
docs(plugin): clarify revocation authority
seonghobae Aug 10, 2026
70785c1
Merge branch 'main' into feat/plugin-installation-persistence
github-actions[bot] Aug 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ jobs:
AI_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/life_os_test
AI_TEST_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/life_os_test
IDENTITY_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/life_os_test
INTEGRATION_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/life_os_test
PLANNING_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/life_os_test
HABIT_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/life_os_test
NOTIFICATION_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/life_os_test
Expand Down Expand Up @@ -208,6 +209,7 @@ jobs:
AI_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/life_os_test
AI_TEST_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/life_os_test
IDENTITY_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/life_os_test
INTEGRATION_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/life_os_test
PLANNING_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/life_os_test
HABIT_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/life_os_test
NOTIFICATION_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/life_os_test
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ All notable changes to LifeOS are documented in this file.

### Added

- Durable PostgreSQL plugin-installation authority with opaque UUIDv4 installation/workspace/installer identity, exact manifest digests, normalized explicit grants, bounded conflict replay, and atomic revocation evidence in the service-owned `plugin_integration` schema.
- A durable PostgreSQL data-rights request ledger with workspace-scoped idempotency, immutable request and terminal receipt digests, one-way completion state, and real integration evidence that erasure receipts survive removal of the source workspace and user.
- Migration `0006_data_rights_request_ledger.sql` for the service-owned identity ledger, retaining only bounded opaque authority references and digest/status/timestamp evidence rather than exported personal payloads.
- An hourly and manually dispatchable NVIDIA NIM live-conformance harness that pins contextual-orchestrator to an exact reviewed commit, compares strong single-route reasoning with bounded conducted workflows, and retains only validated credential-free quality, safety, orchestration, usage, and ablation evidence.
Expand Down Expand Up @@ -36,6 +37,7 @@ All notable changes to LifeOS are documented in this file.

### Security

- Plugin installation lookup, conflict replay, and revocation now carry authenticated workspace and installing-user authority through the PostgreSQL boundary; the durable record contains no plaintext plugin secret, token, credential, or password material.
- The data-rights request ledger keeps personal export payloads out of durable audit rows and normalizes primary-key/idempotency collisions before dependency errors can escape the service boundary.
- The commercial-development model account no longer performs Docker commands, never receives Docker-socket authority, and cannot trigger provider-wide model discovery through the credential bridge.
- The scheduled live-model harness uses only `NVIDIA_NIM_API_KEY`, seeds it through the encrypted contextual-orchestrator credential registry, installs hash-locked dependencies from an exact commit, confines LifeOS traffic to loopback, allowlists NVIDIA NIM egress, and excludes provider credentials, prompts, responses, traces, and hidden reasoning from retained artifacts.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
CREATE SCHEMA IF NOT EXISTS plugin_integration;

CREATE FUNCTION plugin_integration.capability_array_is_valid(capability_values text[])
RETURNS boolean
LANGUAGE sql
IMMUTABLE
STRICT
PARALLEL SAFE
AS $$
SELECT
COALESCE(
bool_and(char_length(capability_name) BETWEEN 1 AND 256),
true
)
AND cardinality(capability_values) = (
SELECT count(DISTINCT capability_name COLLATE "C")
FROM unnest(capability_values) AS capability_name
)
AND capability_values = ARRAY(
SELECT capability_name
FROM unnest(capability_values) AS capability_name
ORDER BY capability_name COLLATE "C"
);
$$;

CREATE TABLE plugin_integration.plugin_installation_record (
installation_id uuid PRIMARY KEY,
workspace_id uuid NOT NULL,
installed_by_user_id uuid NOT NULL,
plugin_id text NOT NULL,
plugin_contract_version text NOT NULL,
manifest_sha256 text NOT NULL,
granted_capabilities text[] NOT NULL,
installation_status text NOT NULL DEFAULT 'active',
installed_at timestamptz NOT NULL,
revoked_at timestamptz,
CONSTRAINT plugin_installation_id_uuid_v4 CHECK (
installation_id::text ~ '^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'
),
CONSTRAINT plugin_installation_workspace_id_uuid_v4 CHECK (
workspace_id::text ~ '^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'
),
CONSTRAINT plugin_installation_user_id_uuid_v4 CHECK (
installed_by_user_id::text ~ '^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'
),
CONSTRAINT plugin_installation_plugin_id_length CHECK (
char_length(plugin_id) BETWEEN 1 AND 256
),
CONSTRAINT plugin_installation_contract_version_length CHECK (
char_length(plugin_contract_version) BETWEEN 1 AND 128
),
CONSTRAINT plugin_installation_manifest_sha256 CHECK (
char_length(manifest_sha256) = 64
AND manifest_sha256 ~ '^[0-9a-f]{64}$'
),
CONSTRAINT plugin_installation_capability_count CHECK (
cardinality(granted_capabilities) BETWEEN 0 AND 32
),
CONSTRAINT plugin_installation_capability_array CHECK (
array_position(granted_capabilities, NULL) IS NULL
AND plugin_integration.capability_array_is_valid(granted_capabilities)
),
CONSTRAINT plugin_installation_status_valid CHECK (
installation_status IN ('active', 'revoked')
),
CONSTRAINT plugin_installation_lifecycle_consistency CHECK (
(revoked_at IS NULL OR revoked_at >= installed_at)
AND (
(installation_status = 'active' AND revoked_at IS NULL)
OR (installation_status = 'revoked' AND revoked_at IS NOT NULL)
)
)
);

CREATE INDEX plugin_installation_workspace_index
ON plugin_integration.plugin_installation_record (workspace_id, installation_status);
266 changes: 266 additions & 0 deletions apps/integration-service/src/plugin-installation-migration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
import { spawnSync } from 'node:child_process';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { beforeEach, describe, expect, it } from 'vitest';

const MIGRATION_PATH = join(
__dirname,
'..',
'migrations',
'0001_plugin_installation_record.sql',
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
const MIGRATION_SQL = readFileSync(MIGRATION_PATH, 'utf8');
const DATABASE_URL = process.env.INTEGRATION_DATABASE_URL;
const describeWithPostgres = DATABASE_URL ? describe : describe.skip;

interface SqlExecution {
readonly status: number | null;
readonly stdout: string;
readonly stderr: string;
}

/** Executes one isolated PostgreSQL client process against the dedicated disposable integration database. */
function executeSql(sql: string): SqlExecution {
if (!DATABASE_URL) {
throw new Error('A dedicated PostgreSQL integration test database URL is required');
}
const target = new URL(DATABASE_URL);
const result = spawnSync(
'psql',
[
'-X',
'-v',
'ON_ERROR_STOP=1',
'-h',
target.hostname,
'-p',
target.port || '5432',
'-U',
decodeURIComponent(target.username),
'-d',
decodeURIComponent(target.pathname.replace(/^\//u, '')),
'-Atq',
],
{
input: sql,
encoding: 'utf8',
env: {
...process.env,
PGPASSWORD: decodeURIComponent(target.password),
},
},
);
if (result.error) {
throw result.error;
}
return {
status: result.status,
stdout: result.stdout,
stderr: result.stderr,
};
}

/** Applies SQL and surfaces only bounded diagnostics when an expected setup fails. */
function requireSqlSuccess(sql: string): string {
const result = executeSql(sql);
if (result.status !== 0) {
throw new Error(`PostgreSQL test setup failed: ${result.stderr.slice(0, 500)}`);
}
return result.stdout.trim();
}

/** Proves that a specific PostgreSQL constraint rejects one fixed hostile fixture. */
function expectSqlFailure(sql: string, expectedConstraint: string): void {
const result = executeSql(sql);
expect(result.status).not.toBe(0);
expect(result.stderr).toContain(expectedConstraint);
}

describe('plugin installation migration contract', () => {
it('uses descriptive multiword database names and stores authority evidence without secret material', () => {
expect(MIGRATION_SQL).toContain(
'CREATE SCHEMA IF NOT EXISTS plugin_integration',
);
expect(MIGRATION_SQL).toContain(
'CREATE TABLE plugin_integration.plugin_installation_record',
);
for (const column of [
'installation_id uuid PRIMARY KEY',
'workspace_id uuid NOT NULL',
'installed_by_user_id uuid NOT NULL',
'plugin_id text NOT NULL',
'plugin_contract_version text NOT NULL',
'manifest_sha256 text NOT NULL',
'granted_capabilities text[] NOT NULL',
"installation_status text NOT NULL DEFAULT 'active'",
'installed_at timestamptz NOT NULL',
'revoked_at timestamptz',
]) {
expect(MIGRATION_SQL).toContain(column);
}
for (const constraint of [
'plugin_installation_id_uuid_v4',
'plugin_installation_workspace_id_uuid_v4',
'plugin_installation_user_id_uuid_v4',
'plugin_installation_manifest_sha256',
'plugin_installation_capability_count',
'plugin_installation_capability_array',
'plugin_installation_lifecycle_consistency',
]) {
expect(MIGRATION_SQL).toContain(`CONSTRAINT ${constraint}`);
}
expect(MIGRATION_SQL).toContain(
'plugin_integration.capability_array_is_valid',
);
expect(MIGRATION_SQL).not.toMatch(
/\b(secret|token|credential|password)_/iu,
);
});
});

describeWithPostgres('plugin installation PostgreSQL constraints', () => {
beforeEach(() => {
requireSqlSuccess('DROP SCHEMA IF EXISTS plugin_integration CASCADE;');
requireSqlSuccess(MIGRATION_SQL);
});

it('rejects impossible lifecycle, digest, capability, and UUID authority evidence', () => {
expectSqlFailure(
`INSERT INTO plugin_integration.plugin_installation_record (
installation_id, workspace_id, installed_by_user_id, plugin_id,
plugin_contract_version, manifest_sha256, granted_capabilities,
installation_status, installed_at, revoked_at
) VALUES (
'11111111-1111-4111-8111-111111111111',
'22222222-2222-4222-8222-222222222222',
'33333333-3333-4333-8333-333333333333',
'example.plugin', '1.0.0', repeat('a', 64), ARRAY['a'],
'active', '2026-08-10T02:00:00.000Z', '2026-08-10T03:00:00.000Z'
);`,
'plugin_installation_lifecycle_consistency',
);

expectSqlFailure(
`INSERT INTO plugin_integration.plugin_installation_record (
installation_id, workspace_id, installed_by_user_id, plugin_id,
plugin_contract_version, manifest_sha256, granted_capabilities,
installation_status, installed_at, revoked_at
) VALUES (
'11111111-1111-4111-8111-111111111112',
'22222222-2222-4222-8222-222222222222',
'33333333-3333-4333-8333-333333333333',
'example.plugin', '1.0.0', repeat('a', 63), ARRAY['a'],
'active', '2026-08-10T02:00:00.000Z', NULL
);`,
'plugin_installation_manifest_sha256',
);

expectSqlFailure(
`INSERT INTO plugin_integration.plugin_installation_record (
installation_id, workspace_id, installed_by_user_id, plugin_id,
plugin_contract_version, manifest_sha256, granted_capabilities,
installation_status, installed_at, revoked_at
) VALUES (
'11111111-1111-4111-8111-111111111113',
'22222222-2222-4222-8222-222222222222',
'33333333-3333-4333-8333-333333333333',
'example.plugin', '1.0.0', repeat('a', 64),
ARRAY(SELECT 'capability.' || lpad(value::text, 2, '0')
FROM generate_series(1, 33) AS value ORDER BY value),
'active', '2026-08-10T02:00:00.000Z', NULL
);`,
'plugin_installation_capability_count',
);

for (const fixture of [
{
sql: `INSERT INTO plugin_integration.plugin_installation_record
VALUES ('11111111-1111-4111-8111-111111111114', '22222222-2222-4222-8222-222222222222',
'33333333-3333-4333-8333-333333333333', 'example.plugin', '1.0.0', repeat('a', 64),
ARRAY[''], 'active', '2026-08-10T02:00:00.000Z', NULL);`,
constraint: 'plugin_installation_capability_array',
},
{
sql: `INSERT INTO plugin_integration.plugin_installation_record
VALUES ('11111111-1111-4111-8111-111111111115', '22222222-2222-4222-8222-222222222222',
'33333333-3333-4333-8333-333333333333', 'example.plugin', '1.0.0', repeat('a', 64),
ARRAY[NULL::text], 'active', '2026-08-10T02:00:00.000Z', NULL);`,
constraint: 'plugin_installation_capability_array',
},
{
sql: `INSERT INTO plugin_integration.plugin_installation_record
VALUES ('11111111-1111-4111-8111-111111111116', '22222222-2222-4222-8222-222222222222',
'33333333-3333-4333-8333-333333333333', 'example.plugin', '1.0.0', repeat('a', 64),
ARRAY[repeat('x', 257)], 'active', '2026-08-10T02:00:00.000Z', NULL);`,
constraint: 'plugin_installation_capability_array',
},
{
sql: `INSERT INTO plugin_integration.plugin_installation_record
VALUES ('11111111-1111-7111-8111-111111111117', '22222222-2222-4222-8222-222222222222',
'33333333-3333-4333-8333-333333333333', 'example.plugin', '1.0.0', repeat('a', 64),
ARRAY['a'], 'active', '2026-08-10T02:00:00.000Z', NULL);`,
constraint: 'plugin_installation_id_uuid_v4',
},
{
sql: `INSERT INTO plugin_integration.plugin_installation_record
VALUES ('11111111-1111-4111-8111-111111111118', '22222222-2222-7222-8222-222222222222',
'33333333-3333-4333-8333-333333333333', 'example.plugin', '1.0.0', repeat('a', 64),
ARRAY['a'], 'active', '2026-08-10T02:00:00.000Z', NULL);`,
constraint: 'plugin_installation_workspace_id_uuid_v4',
},
{
sql: `INSERT INTO plugin_integration.plugin_installation_record
VALUES ('11111111-1111-4111-8111-111111111119', '22222222-2222-4222-8222-222222222222',
'33333333-3333-7333-8333-333333333333', 'example.plugin', '1.0.0', repeat('a', 64),
ARRAY['a'], 'active', '2026-08-10T02:00:00.000Z', NULL);`,
constraint: 'plugin_installation_user_id_uuid_v4',
},
{
sql: `INSERT INTO plugin_integration.plugin_installation_record
VALUES ('11111111-1111-4111-8111-111111111120', '22222222-2222-4222-8222-222222222222',
'33333333-3333-4333-8333-333333333333', 'example.plugin', '1.0.0', repeat('a', 64),
ARRAY['a', 'a'], 'active', '2026-08-10T02:00:00.000Z', NULL);`,
constraint: 'plugin_installation_capability_array',
},
{
sql: `INSERT INTO plugin_integration.plugin_installation_record
VALUES ('11111111-1111-4111-8111-111111111121', '22222222-2222-4222-8222-222222222222',
'33333333-3333-4333-8333-333333333333', 'example.plugin', '1.0.0', repeat('a', 64),
ARRAY['b', 'a'], 'active', '2026-08-10T02:00:00.000Z', NULL);`,
constraint: 'plugin_installation_capability_array',
},
] as const) {
expectSqlFailure(fixture.sql, fixture.constraint);
}

requireSqlSuccess(`INSERT INTO plugin_integration.plugin_installation_record
VALUES ('11111111-1111-4111-8111-111111111122', '22222222-2222-4222-8222-222222222222',
'33333333-3333-4333-8333-333333333333', 'example.plugin', '1.0.0', repeat('a', 64),
ARRAY['a', 'b'], 'active', '2026-08-10T02:00:00.000Z', NULL);`);
});

it('preserves one durable authority row across independent PostgreSQL client processes', () => {
requireSqlSuccess(`
INSERT INTO plugin_integration.plugin_installation_record (
installation_id, workspace_id, installed_by_user_id, plugin_id,
plugin_contract_version, manifest_sha256, granted_capabilities,
installation_status, installed_at, revoked_at
) VALUES (
'11111111-1111-4111-8111-111111111111',
'22222222-2222-4222-8222-222222222222',
'33333333-3333-4333-8333-333333333333',
'example.plugin', '1.0.0', repeat('a', 64), ARRAY['a'],
'active', '2026-08-10T02:00:00.000Z', NULL
);
`);

const durable = requireSqlSuccess(`
SELECT installation_id || '|' || workspace_id || '|' || installed_by_user_id || '|' || installation_status
FROM plugin_integration.plugin_installation_record
WHERE installation_id = '11111111-1111-4111-8111-111111111111'::uuid;
`);
expect(durable).toBe(
'11111111-1111-4111-8111-111111111111|22222222-2222-4222-8222-222222222222|33333333-3333-4333-8333-333333333333|active',
);
});
});
Loading