feat(device-plane): add fail-closed deploy foundation
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
# Device Plane Implementation Baseline
|
||||
|
||||
Status: local fail-closed foundation implemented; no runtime or production
|
||||
mutation.
|
||||
|
||||
## Product boundary
|
||||
|
||||
The Device Manager user interface is a canonical Foundry Page Library
|
||||
template. Foundry owns page instances, layout, presentation and an opaque
|
||||
`device-plane-control` binding. It does not own device records, credentials,
|
||||
raw protocol or command delivery.
|
||||
|
||||
The independent NDC Device Plane owns physical-device state and direct
|
||||
connections:
|
||||
|
||||
```text
|
||||
Foundry Device Manager Page
|
||||
|
|
||||
| device-plane-control (typed server boundary)
|
||||
v
|
||||
Device Control Core <-> Device PostgreSQL
|
||||
|
|
||||
v
|
||||
Device Gateway <-> physical devices
|
||||
```
|
||||
|
||||
Engine L2 may consume safe decoded observations and build workflows/Data
|
||||
Products. It does not own TCP sessions, secrets or the command transport.
|
||||
|
||||
## Preserved production path
|
||||
|
||||
The existing Gelios -> Engine L2 -> External Data Plane -> Foundry Map path is
|
||||
outside this implementation slice. Its credentials, workflows, Data Products,
|
||||
bindings and map presentation must not be changed or restarted by a Device
|
||||
Plane artifact.
|
||||
|
||||
The first B2 pilot adds an NDC server route in parallel and keeps the existing
|
||||
Gelios route unchanged.
|
||||
|
||||
## Source and runtime placement
|
||||
|
||||
Source:
|
||||
|
||||
```text
|
||||
platform/device-plane/
|
||||
packages/device-protocol-contract/
|
||||
packages/arusnavi-b2-adapter/
|
||||
services/device-control-core/
|
||||
services/device-gateway/
|
||||
docker-compose.device-plane.yml
|
||||
```
|
||||
|
||||
Planned Synology runtime:
|
||||
|
||||
```text
|
||||
/volume1/docker/nodedc-device-plane
|
||||
```
|
||||
|
||||
Planned Compose project and services:
|
||||
|
||||
```text
|
||||
nodedc-device-plane
|
||||
device-control-core
|
||||
device-gateway
|
||||
device-postgres
|
||||
```
|
||||
|
||||
`device-postgres` is a private persistent prerequisite. Application overlays
|
||||
must never force-recreate it or its volume.
|
||||
|
||||
The canonical runner selects only `device-control-core` and `device-gateway`
|
||||
with `--no-deps`. Its health acceptance is scoped to the selected services and
|
||||
requires the fail-closed fields to remain disabled. A failed first activation
|
||||
removes only candidate stateless services and never requests volume removal.
|
||||
|
||||
## Network boundary
|
||||
|
||||
The baseline publishes no device port. The planned pilot ingress is
|
||||
`device.nodedc.ru:9921/TCP` only after a port-collision check, quarantine/rate
|
||||
limits and a separate reviewed ingress transition.
|
||||
|
||||
DSM HTTP/HTTPS Reverse Proxy is not a raw TCP ingress and must not be configured
|
||||
as `443 -> 9921`.
|
||||
|
||||
The artifact never changes DSM firewall, DSM Router Configuration, DNS or a
|
||||
physical router.
|
||||
|
||||
## Identity and onboarding
|
||||
|
||||
An IMEI is a claimed protocol identifier, not proof of tenant ownership.
|
||||
|
||||
- An unknown connection produces a quarantine-only discovery.
|
||||
- A discovery never receives commands.
|
||||
- Pilot claim requires an explicit platform-admin action.
|
||||
- Production assignment requires authoritative pre-enrollment or an audited
|
||||
inventory import.
|
||||
- First-claim-wins by IMEI is forbidden.
|
||||
|
||||
The ARUSNAVI Web account login/password is used only by the human operator to
|
||||
configure the additional device route. It is not a Device Plane credential.
|
||||
|
||||
## Protocol evidence
|
||||
|
||||
The official B2 material proves:
|
||||
|
||||
- four simultaneous monitoring server routes;
|
||||
- `INTERNAL`, `EXTERNAL`, `USER_AG` and EGTS variants;
|
||||
- INTERNAL server-side identification by modem IMEI;
|
||||
- server route fields for DNS/IP, TCP port, protocol and optional ID;
|
||||
- SMS/TCP command families and a six-digit device access password.
|
||||
|
||||
The public material inspected so far does not define enough exact INTERNAL
|
||||
packet framing and acknowledgement detail to implement a production decoder.
|
||||
The B2 adapter therefore hashes bounded evidence and fails closed. It does not
|
||||
search arbitrary bytes for a 15-digit sequence and call that an IMEI.
|
||||
|
||||
## Command boundary
|
||||
|
||||
Outbound command transport is disabled in this baseline. No command builder is
|
||||
exported.
|
||||
|
||||
Later lifecycle:
|
||||
|
||||
```text
|
||||
draft -> planned -> awaiting_confirmation -> queued -> dispatched
|
||||
-> acknowledged | failed | expired | unknown
|
||||
```
|
||||
|
||||
`send` is not success. An `unknown` result forbids automatic retry.
|
||||
|
||||
Erase, factory reset, firmware/custom firmware, physical outputs and arbitrary
|
||||
raw TCP remain forbidden until separate reviewed acceptance slices.
|
||||
|
||||
## Implemented local foundation
|
||||
|
||||
- Provider-neutral discovery, contour and opaque Foundry-binding contracts.
|
||||
- B2 model profile with four parallel routes and INTERNAL/IMEI evidence.
|
||||
- PostgreSQL migration for model profiles, contours, quarantine discoveries,
|
||||
claimed devices, Foundry bindings and append-only audit events.
|
||||
- Core health endpoint and an authenticated quarantine-ingest boundary that is
|
||||
disabled unless explicitly enabled with file-backed secrets.
|
||||
- Gateway health endpoint and a loopback-only test listener that records only
|
||||
a bounded evidence hash, sends no acknowledgement and emits no identifier.
|
||||
- Recursive rejection of secret-like fields, raw payloads and command-shaped
|
||||
input in presentation contracts.
|
||||
- Automated contract, adapter, migration, Core and Gateway tests.
|
||||
- Additive `component=device-plane` runner registry with exact roots, builds,
|
||||
services, allowlist/denylist, runner-owned secrets, health contracts and
|
||||
automatic source/runtime rollback.
|
||||
- Deterministic data-only artifact builder and positive/negative regression
|
||||
tests.
|
||||
- Compose foundation with a private internal network, preserved PostgreSQL
|
||||
volume, file-backed database password and loopback-only health publishing.
|
||||
- Exact one-time PostgreSQL bootstrap descriptor, deterministic builder and
|
||||
absence preflight: an existing database container or volume fails closed,
|
||||
and rollback never removes the volume.
|
||||
|
||||
## Next source slice
|
||||
|
||||
1. Review, hash and promote the canonical runner candidate through the
|
||||
standalone root administrative gate; do not stage an application artifact
|
||||
before fresh `verify-install`.
|
||||
2. Build, plan and accept the separate one-time PostgreSQL prerequisite
|
||||
bootstrap artifact; ordinary application artifacts must continue to exclude
|
||||
the database service.
|
||||
3. Obtain or capture the exact official B2 INTERNAL framing and acknowledgement
|
||||
contract; keep ingress disabled until its fixtures pass.
|
||||
4. Add an authenticated internal Core/Gateway discovery boundary and explicit
|
||||
platform-admin claim operation.
|
||||
5. Build, audit and stage the first deterministic application artifact only
|
||||
after the runner extension is installed and verified.
|
||||
@@ -0,0 +1,25 @@
|
||||
# Device Plane baseline test matrix
|
||||
|
||||
| Boundary | Required proof |
|
||||
| --- | --- |
|
||||
| Restricted identity | IMEI accepts exactly 15 decimal digits internally |
|
||||
| Browser projection | Safe discovery view contains only a masked identifier |
|
||||
| Identifier hashing | HMAC digest is deterministic and does not reveal input |
|
||||
| Secret boundary | Secret-like or raw-payload keys are rejected recursively |
|
||||
| Command boundary | Discovery contract rejects command-shaped input |
|
||||
| Framing bound | B2 evidence inspection rejects empty and oversized buffers |
|
||||
| Framing honesty | Unverified B2 bytes return `official_framing_required` |
|
||||
| No identifier guessing | Embedded digit sequences are never returned as IMEI |
|
||||
| Model profile | Four server routes and INTERNAL identification are recorded |
|
||||
| Gelios preservation | Gelios is a parallel route, not a dependency or failover |
|
||||
| Core database secret | Production Compose uses a file-backed password, not a plaintext environment value |
|
||||
| Core health | Database is ready while discovery ingest and command transport remain disabled |
|
||||
| Gateway health | Public ingress, TCP listener and command transport remain disabled |
|
||||
| Compose exposure | Only loopback health ports `18120/18121` are published; raw `9921` is not |
|
||||
| Application service scope | `files.txt` selects only affected Core/Gateway services with `--no-deps` |
|
||||
| Database preservation | Ordinary application artifacts never select `device-postgres` |
|
||||
| Database bootstrap | Exact descriptor selects PostgreSQL only when both container and volume are absent |
|
||||
| Bootstrap rollback | Candidate container may be removed; named volume is never removed |
|
||||
| Artifact policy | `.env`, secrets, runtime state, tests, logs and `node_modules` are excluded |
|
||||
| Artifact reproducibility | Repeated builds for the same patch id are byte-identical |
|
||||
| Runner compatibility | Existing canonical Platform registry tests remain green |
|
||||
Reference in New Issue
Block a user