feat(device-core): add device registry schema

This commit is contained in:
Codex
2026-08-10 17:34:50 +03:00
parent 70bafdd028
commit 9fdaed1c95
3 changed files with 276 additions and 0 deletions
@@ -0,0 +1,208 @@
begin;
create table if not exists device_adapter_packages (
id uuid primary key,
package_key text not null
check (package_key ~ '^[a-z][a-z0-9-]{1,62}$'),
display_name text not null
check (length(btrim(display_name)) between 1 and 160),
publisher_ref text not null
check (length(btrim(publisher_ref)) between 3 and 256),
lifecycle_state text not null default 'active'
check (lifecycle_state in ('active', 'retired')),
created_by_ref text not null
check (length(btrim(created_by_ref)) between 3 and 256),
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
unique (package_key)
);
create table if not exists device_adapter_versions (
id uuid primary key,
adapter_package_id uuid not null references device_adapter_packages(id),
version text not null
check (version ~ '^[0-9]+\.[0-9]+\.[0-9]+([+-][A-Za-z0-9.-]+)?$'),
runtime_package_ref text not null
check (length(btrim(runtime_package_ref)) between 3 and 256),
content_digest text not null
check (content_digest ~ '^sha256:[a-f0-9]{64}$'),
contract_version text not null
check (contract_version ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{2,127}$'),
capabilities text[] not null default '{}'
check (
cardinality(capabilities) <= 64
and array_position(capabilities, null) is null
),
lifecycle_state text not null default 'draft'
check (lifecycle_state in ('draft', 'active', 'retired')),
registered_by_ref text not null
check (length(btrim(registered_by_ref)) between 3 and 256),
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
unique (adapter_package_id, version),
unique (runtime_package_ref, content_digest),
unique (id, adapter_package_id)
);
alter table device_model_profiles
add column if not exists adapter_version_id uuid
references device_adapter_versions(id),
add column if not exists schema_artifact_ref text
check (
schema_artifact_ref is null
or length(btrim(schema_artifact_ref)) between 3 and 256
),
add column if not exists profile_digest text
check (
profile_digest is null
or profile_digest ~ '^sha256:[a-f0-9]{64}$'
),
add column if not exists capabilities text[] not null default '{}'
check (
cardinality(capabilities) <= 64
and array_position(capabilities, null) is null
),
add column if not exists lifecycle_state text not null default 'active'
check (lifecycle_state in ('draft', 'active', 'retired'));
create index if not exists device_model_profiles_adapter_version_idx
on device_model_profiles (adapter_version_id, lifecycle_state, updated_at desc);
create table if not exists device_edges (
id uuid primary key,
edge_key text not null
check (edge_key ~ '^[a-z][a-z0-9-]{1,62}$'),
display_name text not null
check (length(btrim(display_name)) between 1 and 160),
deployment_ref text
check (
deployment_ref is null
or length(btrim(deployment_ref)) between 3 and 256
),
lifecycle_state text not null default 'provisioning'
check (lifecycle_state in ('provisioning', 'active', 'suspended', 'retired')),
created_by_ref text not null
check (length(btrim(created_by_ref)) between 3 and 256),
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
unique (edge_key)
);
create table if not exists device_routes (
id uuid primary key,
project_id uuid not null references device_projects(id),
route_key text not null
check (route_key ~ '^[a-z][a-z0-9-]{1,62}$'),
display_name text not null
check (length(btrim(display_name)) between 1 and 160),
edge_id uuid not null references device_edges(id),
model_profile_ref text not null references device_model_profiles(profile_ref),
listener_ref text not null
check (length(btrim(listener_ref)) between 3 and 256),
protocol text not null
check (protocol ~ '^[A-Z][A-Z0-9_]{0,31}$'),
direction text not null default 'telemetry'
check (direction in ('telemetry', 'bidirectional')),
lifecycle_state text not null default 'draft'
check (lifecycle_state in ('draft', 'active', 'suspended', 'retired')),
created_by_ref text not null
check (length(btrim(created_by_ref)) between 3 and 256),
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
unique (project_id, route_key),
unique (id, project_id),
unique (id, edge_id, project_id),
unique (id, project_id, model_profile_ref)
);
create index if not exists device_routes_edge_state_idx
on device_routes (edge_id, lifecycle_state, updated_at desc);
create index if not exists device_routes_project_state_idx
on device_routes (project_id, lifecycle_state, updated_at desc);
create table if not exists device_sessions (
id uuid primary key,
session_ref text not null
check (length(btrim(session_ref)) between 3 and 256),
edge_id uuid not null,
project_id uuid not null,
route_id uuid not null,
device_id uuid,
protocol text not null
check (protocol ~ '^[A-Z][A-Z0-9_]{0,31}$'),
lifecycle_state text not null default 'connecting'
check (lifecycle_state in ('connecting', 'online', 'closing', 'closed', 'rejected')),
connected_at timestamptz not null,
last_seen_at timestamptz not null,
disconnected_at timestamptz,
close_reason_code text
check (
close_reason_code is null
or close_reason_code ~ '^[a-z][a-z0-9._-]{1,63}$'
),
frame_count bigint not null default 0 check (frame_count >= 0),
byte_count bigint not null default 0 check (byte_count >= 0),
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
unique (edge_id, session_ref),
foreign key (route_id, edge_id, project_id)
references device_routes(id, edge_id, project_id),
foreign key (device_id, project_id)
references device_instances(id, project_id),
check (last_seen_at >= connected_at),
check (
(lifecycle_state in ('connecting', 'online') and disconnected_at is null)
or
(lifecycle_state in ('closing', 'closed', 'rejected'))
)
);
create index if not exists device_sessions_route_state_seen_idx
on device_sessions (route_id, lifecycle_state, last_seen_at desc);
create index if not exists device_sessions_device_seen_idx
on device_sessions (device_id, last_seen_at desc)
where device_id is not null;
create table if not exists device_enrollment_intents (
id uuid primary key,
project_id uuid not null references device_projects(id),
enrollment_key text not null
check (enrollment_key ~ '^[a-z][a-z0-9-]{1,62}$'),
route_id uuid not null,
model_profile_ref text not null references device_model_profiles(profile_ref),
display_name text not null
check (length(btrim(display_name)) between 1 and 160),
expected_identifier_kind text not null
check (expected_identifier_kind ~ '^[a-z][a-z0-9._-]{1,31}$'),
expected_identifier_digest text not null
check (expected_identifier_digest ~ '^hmac-sha256:[a-f0-9]{64}$'),
expected_identifier_masked text not null
check (length(btrim(expected_identifier_masked)) between 4 and 64),
lifecycle_state text not null default 'pending'
check (lifecycle_state in ('pending', 'observed', 'claimed', 'cancelled', 'expired')),
expires_at timestamptz,
claimed_device_id uuid,
created_by_ref text not null
check (length(btrim(created_by_ref)) between 3 and 256),
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
unique (project_id, enrollment_key),
unique (
project_id,
expected_identifier_kind,
expected_identifier_digest,
model_profile_ref
),
foreign key (route_id, project_id, model_profile_ref)
references device_routes(id, project_id, model_profile_ref),
foreign key (claimed_device_id, project_id)
references device_instances(id, project_id),
check (expires_at is null or expires_at > created_at)
);
create index if not exists device_enrollment_intents_project_state_idx
on device_enrollment_intents (project_id, lifecycle_state, updated_at desc);
commit;