feat(api): add device identity guard and Prometheus metrics

- Persist device identity observations and expose fleet identity summaries.

- Block device control actions when identity verification fails.

- Add metrics collection and a Prometheus scrape endpoint.
This commit is contained in:
chick
2026-09-07 00:44:51 +08:00
parent 8406f14469
commit 961eb928c4
14 changed files with 1619 additions and 4 deletions
@@ -66,6 +66,7 @@ describe('database migrations', () => {
'console_auth_config',
'console_auth_sessions',
'device_groups',
'device_identities',
'event_journal',
'instance_tags',
'instances',
@@ -638,6 +638,38 @@ export const MIGRATIONS: readonly Migration[] = [
"ALTER TABLE notification_channels ADD COLUMN secret_fields TEXT NOT NULL DEFAULT ''",
],
},
{
id: 17,
name: 'device-identity-guard',
statements: [
// The Hub refuses to control a device whose identity drifted: the same IMEI claimed by
// two records, or hardware swapped behind an address that is still registered. Both are
// recorded here and only an operator can clear them, because the machine cannot tell
// which of two claimants is the real device.
`CREATE TABLE device_identities (
instance_id TEXT PRIMARY KEY REFERENCES instances(id) ON DELETE CASCADE,
status TEXT NOT NULL DEFAULT 'confirmed' CHECK (status IN ('confirmed','pending')),
reasons TEXT NOT NULL DEFAULT '[]',
imei TEXT NOT NULL DEFAULT '',
manufacturer TEXT NOT NULL DEFAULT '',
model TEXT NOT NULL DEFAULT '',
revision TEXT NOT NULL DEFAULT '',
agent TEXT NOT NULL DEFAULT '',
origin TEXT NOT NULL DEFAULT '',
fingerprint TEXT NOT NULL DEFAULT '',
confirmed_fingerprint TEXT NOT NULL DEFAULT '',
confirmed_values_json TEXT NOT NULL DEFAULT '{}',
confirmed_peers_json TEXT NOT NULL DEFAULT '[]',
detail_json TEXT NOT NULL DEFAULT '{}',
observed_at TEXT NOT NULL,
confirmed_at TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
)`,
'CREATE INDEX idx_device_identities_status ON device_identities(status, instance_id)',
'CREATE INDEX idx_device_identities_imei ON device_identities(imei, instance_id)',
],
},
];
const createMigrationsTable = `CREATE TABLE schema_migrations (