feat(device-core): add control resource ledger

This commit is contained in:
Codex
2026-08-10 19:05:48 +03:00
parent 422ddb020f
commit 43dc9b1f45
15 changed files with 2214 additions and 0 deletions
@@ -0,0 +1,158 @@
import assert from "node:assert/strict";
import test from "node:test";
import {
DEVICE_CONTROL_RESOURCE_COMMAND_KINDS,
normalizeControlResourceManagementCommand,
} from "../src/control-resource-management.mjs";
import {
ALL_DEVICE_MANAGEMENT_COMMAND_KINDS,
normalizeDeviceManagementCommand,
} from "../src/management-command.mjs";
const projectRef = "project:11111111-1111-4111-8111-111111111111";
const deviceRef = "device:22222222-2222-4222-8222-222222222222";
const collectionRef = "collection:33333333-3333-4333-8333-333333333333";
const bindingRef = "binding:44444444-4444-4444-8444-444444444444";
const revisionRef =
"configuration-revision:55555555-5555-4555-8555-555555555555";
test("control resource commands join the strict idempotent surface", () => {
for (const kind of DEVICE_CONTROL_RESOURCE_COMMAND_KINDS) {
assert.equal(ALL_DEVICE_MANAGEMENT_COMMAND_KINDS.includes(kind), true);
}
assert.equal(
normalizeDeviceManagementCommand(
"device_binding.ensure",
bindingInput(),
).projectId,
projectRef.slice("project:".length),
);
});
test("binding input is source-scoped and cannot claim external approval", () => {
const command = normalizeControlResourceManagementCommand(
"device_binding.ensure",
bindingInput(),
);
assert.deepEqual(command.source, {
kind: "collection",
id: collectionRef.slice("collection:".length),
});
assert.deepEqual(command.capabilities, ["inspect", "observe"]);
assert.equal("lifecycleState" in command, false);
assert.equal("externalApprovalRef" in command, false);
assert.throws(
() => normalizeControlResourceManagementCommand(
"device_binding.ensure",
{ ...bindingInput(), externalApprovalRef: "approval:forged" },
),
/device_management_command_field_unexpected:externalApprovalRef/,
);
assert.throws(
() => normalizeControlResourceManagementCommand(
"device_binding.ensure",
{ ...bindingInput(), targetRef: "ndc-credref:must-not-be-a-target" },
),
/device_binding_target_ref_invalid/,
);
});
test("binding revoke uses only project, binding and bounded reason refs", () => {
const command = normalizeControlResourceManagementCommand(
"device_binding.revoke",
{
projectRef,
bindingRef,
resolutionCode: "operator.unbound",
},
);
assert.equal(command.bindingId, bindingRef.slice("binding:".length));
assert.equal(command.resolutionCode, "operator.unbound");
});
test("configuration is canonical, bounded and secret-free before hashing", () => {
const first = normalizeControlResourceManagementCommand(
"device_configuration_revision.create",
{
projectRef,
deviceRef,
configuration: {
reporting_interval_seconds: 15,
motion: { enabled: true, threshold: 3.5 },
channels: ["gps", "voltage"],
},
changeSummary: "Pilot reporting profile",
},
);
const reordered = normalizeControlResourceManagementCommand(
"device_configuration_revision.create",
{
projectRef,
deviceRef,
configuration: {
channels: ["gps", "voltage"],
motion: { threshold: 3.5, enabled: true },
reporting_interval_seconds: 15,
},
changeSummary: "Pilot reporting profile",
},
);
assert.equal(first.configurationDigest, reordered.configurationDigest);
assert.equal(Object.isFrozen(first.configuration.motion), true);
assert.throws(
() => normalizeControlResourceManagementCommand(
"device_configuration_revision.create",
{
projectRef,
deviceRef,
configuration: { api_token: "forbidden" },
},
),
/forbidden_device_field/,
);
assert.throws(
() => normalizeControlResourceManagementCommand(
"device_configuration_revision.create",
{
projectRef,
deviceRef,
configuration: { tracker_imei: "000000000000001" },
},
),
/safe_projection_contains_unmasked_imei/,
);
});
test("desired configuration binds one exact immutable revision", () => {
const command = normalizeControlResourceManagementCommand(
"device_configuration_desired.set",
{
projectRef,
deviceRef,
configurationRevisionRef: revisionRef,
},
);
assert.equal(command.deviceId, deviceRef.slice("device:".length));
assert.equal(
command.configurationRevisionId,
revisionRef.slice("configuration-revision:".length),
);
assert.equal("applied" in command, false);
});
function bindingInput() {
return {
projectRef,
bindingKey: "robot2b-map",
displayName: "Robot2B map binding",
source: { kind: "collection", ref: collectionRef },
targetKind: "foundry.application",
targetRef: "foundry-application:robot2b-test",
capabilities: ["observe", "inspect"],
};
}
@@ -0,0 +1,104 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
const schemaUrl = new URL(
"../migrations/010_device_control_resources.sql",
import.meta.url,
);
const commandsUrl = new URL(
"../migrations/011_device_control_resource_commands.sql",
import.meta.url,
);
const repositoryUrl = new URL("../src/postgres-repository.mjs", import.meta.url);
const appUrl = new URL("../src/app.mjs", import.meta.url);
test("control resource schema separates bindings, revisions and current state", async () => {
const sql = await readFile(schemaUrl, "utf8");
assert.match(sql, /create table if not exists device_resource_bindings/);
assert.match(sql, /pending_external_approval/);
assert.match(sql, /external_approval_digest/);
assert.match(sql, /device_binding_source_scope_mismatch/);
assert.match(sql, /create table if not exists device_configuration_revisions/);
assert.match(sql, /create table if not exists device_configuration_state/);
assert.match(sql, /device_configuration_revision_scope_mismatch/);
assert.match(sql, /unique \(device_id, revision_number\)/);
});
test("command ledger has honest ordered states without a transport API", async () => {
const sql = await readFile(schemaUrl, "utf8");
const app = await readFile(appUrl, "utf8");
assert.match(sql, /create table if not exists device_commands/);
assert.match(sql, /create table if not exists device_command_events/);
for (const state of [
"draft",
"planned",
"awaiting_confirmation",
"queued",
"dispatched",
"acknowledged",
"verified",
"failed",
"expired",
"unknown",
]) {
assert.match(sql, new RegExp(`'${state}'`));
}
assert.match(sql, /device_command_initial_event_invalid/);
assert.match(sql, /device_command_event_sequence_invalid/);
assert.match(sql, /device_command_event_transition_invalid/);
assert.match(sql, /device_command_event_projection_mismatch/);
assert.match(sql, /device_command_events_current_projection_guard/);
assert.match(sql, /device_command_current_projection_mismatch/);
assert.doesNotMatch(app, /device-commands:(?:plan|confirm|dispatch)/);
});
test("configuration, command history and audit are append-only", async () => {
const sql = await readFile(schemaUrl, "utf8");
for (const table of [
"device_configuration_revisions",
"device_command_events",
"device_audit_events",
]) {
assert.match(
sql,
new RegExp(`${table}_immutable_guard[\\s\\S]*before update or delete or truncate`),
);
}
assert.match(sql, /device_immutable_record_mutation_forbidden/);
assert.match(sql, /device_transfer_active_resource_binding/);
assert.match(sql, /device_transfer_applied_configuration/);
assert.match(sql, /device_transfer_nonterminal_command/);
});
test("control resource schema contains no seeded device or raw secret material", async () => {
const sql = await readFile(schemaUrl, "utf8");
assert.doesNotMatch(sql, /insert\s+into/i);
assert.doesNotMatch(sql, /dcctouch|arusnavi|gelios|\bb2\b|imei/i);
assert.doesNotMatch(sql, /password\s+text|token\s+text|secret\s+text|raw_command|raw_packet/i);
});
test("commands extend receipts only after their schema", async () => {
const commands = await readFile(commandsUrl, "utf8");
const repository = await readFile(repositoryUrl, "utf8");
for (const kind of [
"device_binding.ensure",
"device_binding.revoke",
"device_configuration_revision.create",
"device_configuration_desired.set",
]) {
assert.match(commands, new RegExp(`'${kind.replace(".", "\\.")}'`));
}
const schemaIndex = repository.indexOf("010_device_control_resources.sql");
const commandsIndex = repository.indexOf(
"011_device_control_resource_commands.sql",
);
assert.notEqual(schemaIndex, -1);
assert.notEqual(commandsIndex, -1);
assert.ok(schemaIndex < commandsIndex);
});
@@ -0,0 +1,313 @@
import assert from "node:assert/strict";
import test from "node:test";
import { assertSafeProjection } from "../../../packages/device-protocol-contract/src/index.mjs";
import { normalizeDeviceManagementCommand } from "../src/management-command.mjs";
import { PostgresDeviceRepository } from "../src/postgres-repository.mjs";
import { normalizeManagementActor } from "../src/project-management.mjs";
const now = new Date("2026-08-10T00:00:00.000Z");
const projectId = "11111111-1111-4111-8111-111111111111";
const ownerId = "22222222-2222-4222-8222-222222222222";
const deviceId = "33333333-3333-4333-8333-333333333333";
const collectionId = "44444444-4444-4444-8444-444444444444";
const bindingId = "55555555-5555-4555-8555-555555555555";
const revisionId = "66666666-6666-4666-8666-666666666666";
test("creates only a pending collection binding owned by the source project", async () => {
const actor = managementActor();
const command = normalizeDeviceManagementCommand("device_binding.ensure", {
projectRef: `project:${projectId}`,
bindingKey: "robot2b-map",
displayName: "Robot2B map binding",
source: { kind: "collection", ref: `collection:${collectionId}` },
targetKind: "foundry.application",
targetRef: "foundry-application:robot2b-test",
capabilities: ["observe", "inspect"],
});
const client = scriptedClient([
step("begin"),
receiptStep("receipt-binding"),
projectStep(),
grantsStep(actor),
step("from device_collections", {
rows: [{ id: collectionId, project_id: projectId, lifecycle_state: "active" }],
}),
step("insert into device_resource_bindings", {
rows: [bindingRow({ source_kind: "collection", device_id: null })],
}),
step("insert into device_audit_events"),
step("update device_management_command_receipts"),
step("commit"),
]);
const repository = repositoryWithClient(client);
const result = await repository.executeManagementCommand(commandInput({
actor,
commandKind: "device_binding.ensure",
command,
digestCharacter: "a",
}));
assert.equal(result.result.binding.lifecycleState, "pending_external_approval");
assert.equal(result.result.binding.source.ref, `collection:${collectionId}`);
assert.equal("externalApprovalRef" in result.result.binding, false);
assertSafeProjection(result.result);
assert.equal(client.remaining(), 0);
});
test("creates an immutable configuration revision from the active profile schema", async () => {
const actor = managementActor();
const command = createConfigurationCommand();
const client = scriptedClient([
step("begin"),
receiptStep("receipt-configuration-revision"),
projectStep(),
grantsStep(actor),
step("from device_instances", { rows: [deviceRow()] }),
step("from device_model_profiles", {
rows: [{
profile_ref: "vendor.model.protocol.v1",
schema_artifact_ref: "schema:vendor.model.protocol.v1",
lifecycle_state: "active",
}],
}),
step("from device_configuration_revisions", {
rows: [{ next_revision: "1" }],
}),
step("insert into device_configuration_revisions", {
rows: [configurationRevisionRow({
configuration_digest: command.configurationDigest,
configuration: command.configuration,
})],
}),
step("insert into device_audit_events"),
step("update device_management_command_receipts"),
step("commit"),
]);
const repository = repositoryWithClient(client);
const result = await repository.executeManagementCommand(commandInput({
actor,
commandKind: "device_configuration_revision.create",
command,
digestCharacter: "b",
}));
assert.equal(result.result.configurationRevision.revisionNumber, 1);
assert.deepEqual(result.result.configurationRevision.configuration, {
reporting_interval_seconds: 15,
});
assert.equal(
result.result.configurationRevision.schemaArtifactRef,
"schema:vendor.model.protocol.v1",
);
assertSafeProjection(result.result);
assert.equal(client.remaining(), 0);
});
test("sets desired configuration without claiming runtime apply", async () => {
const actor = managementActor();
const command = normalizeDeviceManagementCommand(
"device_configuration_desired.set",
{
projectRef: `project:${projectId}`,
deviceRef: `device:${deviceId}`,
configurationRevisionRef: `configuration-revision:${revisionId}`,
},
);
const client = scriptedClient([
step("begin"),
receiptStep("receipt-configuration-desired"),
projectStep(),
grantsStep(actor),
step("from device_instances", { rows: [deviceRow()] }),
step("from device_configuration_revisions", {
rows: [configurationRevisionRow()],
}),
step("from device_configuration_state", { rows: [] }),
step("insert into device_configuration_state", {
rows: [{
device_id: deviceId,
project_id: projectId,
desired_revision_id: revisionId,
applied_revision_id: null,
}],
}),
step("insert into device_audit_events"),
step("update device_management_command_receipts"),
step("commit"),
]);
const repository = repositoryWithClient(client);
const result = await repository.executeManagementCommand(commandInput({
actor,
commandKind: "device_configuration_desired.set",
command,
digestCharacter: "c",
}));
assert.equal(result.result.changed, true);
assert.equal(
result.result.configurationState.desiredConfigurationRevisionRef,
`configuration-revision:${revisionId}`,
);
assert.equal(
result.result.configurationState.appliedConfigurationRevisionRef,
null,
);
assert.equal("applied" in result.result, false);
assert.equal(client.remaining(), 0);
});
function createConfigurationCommand() {
return normalizeDeviceManagementCommand(
"device_configuration_revision.create",
{
projectRef: `project:${projectId}`,
deviceRef: `device:${deviceId}`,
configuration: { reporting_interval_seconds: 15 },
changeSummary: "Pilot reporting profile",
},
);
}
function managementActor() {
return normalizeManagementActor({
userRef: "user:device-engineer",
hubRole: "member",
groupRefs: [],
ownerScopes: [],
});
}
function projectStep() {
return step("from device_projects p", {
rows: [{
id: projectId,
owner_scope_id: ownerId,
lifecycle_state: "active",
scope_kind: "company",
owner_ref: "client:example-company",
owner_display_name: "Example Company",
owner_lifecycle_state: "active",
}],
});
}
function grantsStep(actor) {
return step("from device_project_grants", {
rows: [{
id: "77777777-7777-4777-8777-777777777777",
principal_kind: "user",
principal_ref: actor.userRef,
project_role: "engineer",
capability_allow: [],
capability_deny: [],
lifecycle_state: "active",
}],
});
}
function deviceRow() {
return {
id: deviceId,
contour_id: null,
owner_scope_id: ownerId,
project_id: projectId,
model_profile_ref: "vendor.model.protocol.v1",
lifecycle_state: "claimed",
};
}
function bindingRow(overrides = {}) {
return {
id: bindingId,
owner_scope_id: ownerId,
project_id: projectId,
binding_key: "robot2b-map",
display_name: "Robot2B map binding",
source_kind: "device",
device_id: deviceId,
collection_id: collectionId,
target_kind: "foundry.application",
target_ref: "foundry-application:robot2b-test",
capabilities: ["inspect", "observe"],
lifecycle_state: "pending_external_approval",
source_approved_at: now,
created_at: now,
updated_at: now,
created: true,
...overrides,
};
}
function configurationRevisionRow(overrides = {}) {
return {
id: revisionId,
owner_scope_id: ownerId,
project_id: projectId,
device_id: deviceId,
revision_number: "1",
model_profile_ref: "vendor.model.protocol.v1",
schema_artifact_ref: "schema:vendor.model.protocol.v1",
configuration_digest: `sha256:${"d".repeat(64)}`,
configuration: { reporting_interval_seconds: 15 },
change_summary: "Pilot reporting profile",
created_at: now,
...overrides,
};
}
function receiptStep(id) {
return step("insert into device_management_command_receipts", {
rows: [{ id }],
});
}
function commandInput({ actor, commandKind, command, digestCharacter }) {
return {
idempotencyKey: `phase25-${commandKind.replaceAll(".", "-")}-0001`,
commandKind,
requestDigest: `sha256:${digestCharacter.repeat(64)}`,
actor,
command,
};
}
function repositoryWithClient(client) {
return new PostgresDeviceRepository({
pool: {
query: async () => ({ rows: [] }),
connect: async () => client,
end: async () => undefined,
},
});
}
function step(includes, result = { rows: [] }) {
return { includes, result };
}
function scriptedClient(steps) {
const queue = [...steps];
return {
released: false,
async query(sql) {
const next = queue.shift();
assert.ok(next, `Unexpected query: ${sql}`);
assert.match(String(sql), new RegExp(escapeRegExp(next.includes), "i"));
return next.result;
},
release() {
this.released = true;
},
remaining() {
return queue.length;
},
};
}
function escapeRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
@@ -111,6 +111,10 @@ test("authorized transfer preserves history and detaches source collections", as
grantsStep(actor, "owner"),
step("from device_sessions", { rows: [{ active: false }] }),
step("from device_credential_bindings", { rows: [{ active: false }] }),
step("from device_resource_bindings", { rows: [{ active: false }] }),
step("from device_configuration_state", { rows: [] }),
step("from device_commands", { rows: [{ active: false }] }),
step("delete from device_configuration_state", { rows: [], rowCount: 0 }),
step("delete from device_collection_members", { rows: [], rowCount: 2 }),
step("update device_instances", {
rows: [deviceRow({
@@ -139,6 +143,7 @@ test("authorized transfer preserves history and detaches source collections", as
assert.equal(result.result.device.projectRef, `project:${targetProjectId}`);
assert.equal(result.result.detachedCollectionCount, 2);
assert.equal(result.result.transferredIdentifierCount, 1);
assert.equal(result.result.clearedDesiredConfiguration, false);
assert.equal(client.remaining(), 0);
assert.equal(client.released, true);
});
@@ -173,6 +178,104 @@ test("transfer fails closed while a credential binding is active", async () => {
assert.equal(client.released, true);
});
test("transfer fails closed while a resource binding is pending approval", async () => {
const actor = managementActor("owner");
const command = transferCommand();
const client = scriptedClient([
step("begin"),
receiptStep("receipt-transfer-resource-bound"),
step("from device_instances", { rows: [deviceRow()] }),
projectStep(sourceProjectId, sourceOwnerId),
grantsStep(actor, "owner"),
projectStep(targetProjectId, targetOwnerId),
grantsStep(actor, "owner"),
step("from device_sessions", { rows: [{ active: false }] }),
step("from device_credential_bindings", { rows: [{ active: false }] }),
step("from device_resource_bindings", { rows: [{ active: true }] }),
step("rollback"),
]);
const repository = repositoryWithClient(client);
await assert.rejects(
repository.executeManagementCommand(commandInput({
actor,
commandKind: "device.transfer",
command,
digestCharacter: "1",
})),
/device_transfer_active_resource_binding/,
);
assert.equal(client.remaining(), 0);
});
test("transfer fails closed with applied configuration", async () => {
const actor = managementActor("owner");
const command = transferCommand();
const client = scriptedClient([
step("begin"),
receiptStep("receipt-transfer-applied-config"),
step("from device_instances", { rows: [deviceRow()] }),
projectStep(sourceProjectId, sourceOwnerId),
grantsStep(actor, "owner"),
projectStep(targetProjectId, targetOwnerId),
grantsStep(actor, "owner"),
step("from device_sessions", { rows: [{ active: false }] }),
step("from device_credential_bindings", { rows: [{ active: false }] }),
step("from device_resource_bindings", { rows: [{ active: false }] }),
step("from device_configuration_state", {
rows: [{
desired_revision_id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
applied_revision_id: "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
}],
}),
step("rollback"),
]);
const repository = repositoryWithClient(client);
await assert.rejects(
repository.executeManagementCommand(commandInput({
actor,
commandKind: "device.transfer",
command,
digestCharacter: "2",
})),
/device_transfer_applied_configuration/,
);
assert.equal(client.remaining(), 0);
});
test("transfer fails closed with a nonterminal command", async () => {
const actor = managementActor("owner");
const command = transferCommand();
const client = scriptedClient([
step("begin"),
receiptStep("receipt-transfer-command-active"),
step("from device_instances", { rows: [deviceRow()] }),
projectStep(sourceProjectId, sourceOwnerId),
grantsStep(actor, "owner"),
projectStep(targetProjectId, targetOwnerId),
grantsStep(actor, "owner"),
step("from device_sessions", { rows: [{ active: false }] }),
step("from device_credential_bindings", { rows: [{ active: false }] }),
step("from device_resource_bindings", { rows: [{ active: false }] }),
step("from device_configuration_state", { rows: [] }),
step("from device_commands", { rows: [{ active: true }] }),
step("rollback"),
]);
const repository = repositoryWithClient(client);
await assert.rejects(
repository.executeManagementCommand(commandInput({
actor,
commandKind: "device.transfer",
command,
digestCharacter: "3",
})),
/device_transfer_nonterminal_command/,
);
assert.equal(client.remaining(), 0);
});
test("reject resolves both quarantine and enrollment without exposing a digest", async () => {
const actor = managementActor("member");
const command = normalizeDeviceManagementCommand("discovery.reject", {
@@ -15,8 +15,11 @@ test("management surface is internal, POST-only and disabled by default", async
assert.match(source, /\/internal\/v1\/management\/projects:ensure/);
assert.match(source, /\/internal\/v1\/management\/collections:ensure/);
assert.match(source, /\/internal\/v1\/management\/project-grants:upsert/);
assert.match(source, /\/internal\/v1\/management\/device-bindings:ensure/);
assert.match(source, /\/internal\/v1\/management\/device-configuration-revisions:create/);
assert.match(source, /request\.method === "POST" && managementCommandKind/);
assert.doesNotMatch(source, /\/api\/public\/.*management/);
assert.doesNotMatch(source, /device-commands:(?:plan|confirm|dispatch)/);
});
test("management token remains file-backed and is not enabled by current Compose", async () => {
@@ -138,6 +138,28 @@ test("matching group grants combine bounded operator and engineer capabilities",
assert.equal(access.capabilities.includes("access.manage"), false);
});
test("configuration mutation belongs to engineer and admin, not operator", () => {
const engineer = resolveProjectAccess({
actor: actor({ groupRefs: ["group:engineers"] }),
grants: [grant({
principalKind: "group",
principalRef: "group:engineers",
projectRole: "engineer",
})],
});
const operator = resolveProjectAccess({
actor: actor({ groupRefs: ["group:operators"] }),
grants: [grant({
principalKind: "group",
principalRef: "group:operators",
projectRole: "operator",
})],
});
assert.equal(engineer.capabilities.includes("configuration.manage"), true);
assert.equal(operator.capabilities.includes("configuration.manage"), false);
});
test("Hub ceiling and explicit deny prevent privilege escalation", () => {
const ownerGrant = grant({
grantRef: "grant:owner",