@medicine-wheel/infra 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,140 @@
1
+ # @medicine-wheel/infra
2
+
3
+ Typed infrastructure facets for the Medicine Wheel Developer Suite.
4
+
5
+ A host, a tenant, and a service are already beings the suite can reason about —
6
+ `land`, `human`, `knowledge` in the closed `NodeType` union. What has had no home
7
+ is their machine specifics: the port, the unit name, whether linger is on. Those
8
+ leak into `metadata: Record<string, unknown>`, where nothing can check them.
9
+
10
+ This package gives them a typed home **keyed by node id**, so a systemd unit
11
+ reads as a being with the same grammar as a ceremony — and so `ontology-core`
12
+ does not have to change to make that true.
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ npm install @medicine-wheel/infra
18
+ ```
19
+
20
+ Depends only on `@medicine-wheel/ontology-core` and `zod`.
21
+
22
+ ## What is here
23
+
24
+ ```ts
25
+ import {
26
+ detectPortConflicts,
27
+ type HostFacet,
28
+ type TenantFacet,
29
+ type ServiceFacet,
30
+ type PortBinding,
31
+ type MetisHold,
32
+ } from '@medicine-wheel/infra';
33
+ ```
34
+
35
+ A **facet annotates an existing `RelationalNode` by id** — it never re-declares
36
+ one, and it inherits `direction` from the node rather than re-encoding it.
37
+
38
+ | Facet | Annotates a node of type | Direction it serves |
39
+ |---|---|---|
40
+ | `HostFacet` | `land` | — the machine itself |
41
+ | `TenantFacet` | `human` | South — an account is a being, and `linger` is a precondition |
42
+ | `ServiceFacet` | `knowledge` | West — the thing that executes |
43
+ | `PortBinding` | *(carried by `ServiceFacet`)* | South — a claim that must hold before West runs |
44
+
45
+ Every facet carries an optional `MetisHold`.
46
+
47
+ ## `detectPortConflicts`
48
+
49
+ ```ts
50
+ const conflicts = detectPortConflicts([...declared, ...observed]);
51
+ // [{ host: 'node:land:eury', port: 443, proto: 'tcp',
52
+ // claimants: ['node:knowledge:ava:...', 'node:knowledge:gmusic:...'] }, ...]
53
+ ```
54
+
55
+ Declared bindings are authored by eury; observed ones are read live from systemd
56
+ by gaia. Neither side alone can see a collision — this is the traversal over
57
+ their union, so the collision returns **before** the second tenant is
58
+ provisioned instead of at deploy time.
59
+
60
+ Three properties are load-bearing:
61
+
62
+ - **One service seen twice is not a conflict.** The input is declared ∪
63
+ observed, so the same claim arrives from both sides. Claimants de-duplicate by
64
+ `boundBy`; only two *distinct* services collide.
65
+ - **Ports are scarce per host, not globally.** `:4444` on `eury` and `:4444` on
66
+ `gaia` are not in tension.
67
+ - **Output is deterministic** — sorted by host, then proto, then port, with
68
+ claimants sorted inside each row — so two runs can be diffed.
69
+
70
+ A binding with no `proto` is read as `tcp`, so shorthand and longhand tcp claims
71
+ collide with each other while tcp and udp on the same number do not.
72
+
73
+ `PortConflict` carries a `proto` field that the specification's shape (§S3) does
74
+ not name. Without it, a host colliding on both tcp/53 and udp/53 returns two rows
75
+ that cannot be told apart.
76
+
77
+ ## `MetisHold` — what the grid must not flatten
78
+
79
+ The four-directions ontology is an imposed legibility grid. Legibility is what
80
+ lets an operator reason at all; a grid that becomes the *only* sanctioned way to
81
+ describe a system erases the métis — the "restart it twice," the "that BSSID lock
82
+ is load-bearing," the credential path everyone just knows.
83
+
84
+ ```ts
85
+ const zulip: ServiceFacet = {
86
+ // ...
87
+ execStop: 'docker compose stop',
88
+ metis: {
89
+ exceptions: ['compose stop, never down — down drops the volumes'],
90
+ heldBy: 'William',
91
+ },
92
+ };
93
+ ```
94
+
95
+ `heldBy` names a person on purpose. Invisible work gets surfaced with its
96
+ carrier attached, not anonymised into the schema.
97
+
98
+ ## Validation at the boundary
99
+
100
+ Every facet ships a Zod schema, because a legible-but-stale model lies with a
101
+ straight face. Parse an emitted manifest before trusting it:
102
+
103
+ ```ts
104
+ import { ServiceFacetSchema } from '@medicine-wheel/infra';
105
+
106
+ const parsed = ServiceFacetSchema.safeParse(emitted);
107
+ if (!parsed.success) { /* the emitter misread systemd — do not reconcile on this */ }
108
+ ```
109
+
110
+ Port numbers are integers in 1–65535. A misparsed `0` is rejected here rather
111
+ than silently never colliding with anything downstream.
112
+
113
+ ## Scope of `0.1.0`
114
+
115
+ Ships S1, S3, S5 only — facets, `detectPortConflicts`, `MetisHold`, schemas.
116
+ Deliberately **not** here:
117
+
118
+ - **`0.2.0`** (S4) — `Precondition`, `preconditionGuard`, `readyService`. Where
119
+ the machine fact `linger` and the human, withdrawable `ConsentRecord` that
120
+ authorized the root step reference each other by id without either collapsing
121
+ into the other.
122
+ - **`0.3.0`** (S6) — level-triggered `reconcile()` + `ObservedState` and the four
123
+ drift states, which keep `satisfied` from becoming a stale snapshot.
124
+ - **`ontology-core@0.6.0`** (S2) — `part-of` / `ordered-after` / `binds-port` in
125
+ the governed `KINSHIP_EDGE_TYPES` registry. Until then a dependency edge is an
126
+ ordinary `Relation`; `infra` does not fork the vocabulary.
127
+
128
+ Each step makes the next honest. None is a backlog item for this one.
129
+
130
+ ## Grounding
131
+
132
+ - Specification: `/opt/eury/rispecs/foundations/02-specifications.md` §S1, §S3, §S5
133
+ - Argument: `/opt/eury/foundations/relational-infrastructure/synthesis.md`
134
+ - The collision this exists to catch: `jgwill/gaia#75`
135
+ - The outage that motivated observed state: `jgwill/gaia#74`
136
+ - Consumer requirements: `jgwill/eury#3`
137
+
138
+ ## License
139
+
140
+ MIT — IAIP Collaborative, Shawinigan, QC.
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @medicine-wheel/infra
3
+ *
4
+ * Typed infrastructure facets for the Medicine Wheel Developer Suite — a home
5
+ * for the machine specifics (`port`, `unit`, `linger`) that otherwise leak into
6
+ * `metadata: Record<string, unknown>`.
7
+ *
8
+ * A new sibling package built **on** `ontology-core`, exactly as
9
+ * `consent-lifecycle` and `transformation-tracker` are. It changes no published
10
+ * package: the closed `NodeType` union is reused, never widened, and facets are
11
+ * keyed by node id rather than re-declaring nodes.
12
+ *
13
+ * Scope of `0.1.0` — S1, S3, S5 only. `Precondition` / `preconditionGuard` /
14
+ * `readyService` are `0.2.0` (S4); `reconcile()` / `ObservedState` are `0.3.0`
15
+ * (S6). Neither is shipped here.
16
+ *
17
+ * @packageDocumentation
18
+ */
19
+ export type { NodeId, MetisHold, LingerState, Reachability, PortProto, ServiceScope, HostFacet, TenantFacet, ServiceFacet, PortBinding, PortConflict, FacetKind, } from './types';
20
+ export { DEFAULT_PORT_PROTO, FACET_NODE_TYPES } from './types';
21
+ export { MetisHoldSchema, LingerStateSchema, ReachabilitySchema, PortProtoSchema, ServiceScopeSchema, PortNumberSchema, PortBindingSchema, PortConflictSchema, HostFacetSchema, TenantFacetSchema, ServiceFacetSchema, } from './schemas';
22
+ export type { ValidatedMetisHold, ValidatedPortBinding, ValidatedPortConflict, ValidatedHostFacet, ValidatedTenantFacet, ValidatedServiceFacet, } from './schemas';
23
+ export { detectPortConflicts } from './ports';
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,YAAY,EACV,MAAM,EACN,SAAS,EACT,WAAW,EACX,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,SAAS,EACT,WAAW,EACX,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,SAAS,GACV,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAG/D,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AAEnB,YAAY,EACV,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ /**
3
+ * @medicine-wheel/infra
4
+ *
5
+ * Typed infrastructure facets for the Medicine Wheel Developer Suite — a home
6
+ * for the machine specifics (`port`, `unit`, `linger`) that otherwise leak into
7
+ * `metadata: Record<string, unknown>`.
8
+ *
9
+ * A new sibling package built **on** `ontology-core`, exactly as
10
+ * `consent-lifecycle` and `transformation-tracker` are. It changes no published
11
+ * package: the closed `NodeType` union is reused, never widened, and facets are
12
+ * keyed by node id rather than re-declaring nodes.
13
+ *
14
+ * Scope of `0.1.0` — S1, S3, S5 only. `Precondition` / `preconditionGuard` /
15
+ * `readyService` are `0.2.0` (S4); `reconcile()` / `ObservedState` are `0.3.0`
16
+ * (S6). Neither is shipped here.
17
+ *
18
+ * @packageDocumentation
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.detectPortConflicts = exports.ServiceFacetSchema = exports.TenantFacetSchema = exports.HostFacetSchema = exports.PortConflictSchema = exports.PortBindingSchema = exports.PortNumberSchema = exports.ServiceScopeSchema = exports.PortProtoSchema = exports.ReachabilitySchema = exports.LingerStateSchema = exports.MetisHoldSchema = exports.FACET_NODE_TYPES = exports.DEFAULT_PORT_PROTO = void 0;
22
+ var types_1 = require("./types");
23
+ Object.defineProperty(exports, "DEFAULT_PORT_PROTO", { enumerable: true, get: function () { return types_1.DEFAULT_PORT_PROTO; } });
24
+ Object.defineProperty(exports, "FACET_NODE_TYPES", { enumerable: true, get: function () { return types_1.FACET_NODE_TYPES; } });
25
+ // ── Zod schemas ─────────────────────────────────────────────────────────────
26
+ var schemas_1 = require("./schemas");
27
+ Object.defineProperty(exports, "MetisHoldSchema", { enumerable: true, get: function () { return schemas_1.MetisHoldSchema; } });
28
+ Object.defineProperty(exports, "LingerStateSchema", { enumerable: true, get: function () { return schemas_1.LingerStateSchema; } });
29
+ Object.defineProperty(exports, "ReachabilitySchema", { enumerable: true, get: function () { return schemas_1.ReachabilitySchema; } });
30
+ Object.defineProperty(exports, "PortProtoSchema", { enumerable: true, get: function () { return schemas_1.PortProtoSchema; } });
31
+ Object.defineProperty(exports, "ServiceScopeSchema", { enumerable: true, get: function () { return schemas_1.ServiceScopeSchema; } });
32
+ Object.defineProperty(exports, "PortNumberSchema", { enumerable: true, get: function () { return schemas_1.PortNumberSchema; } });
33
+ Object.defineProperty(exports, "PortBindingSchema", { enumerable: true, get: function () { return schemas_1.PortBindingSchema; } });
34
+ Object.defineProperty(exports, "PortConflictSchema", { enumerable: true, get: function () { return schemas_1.PortConflictSchema; } });
35
+ Object.defineProperty(exports, "HostFacetSchema", { enumerable: true, get: function () { return schemas_1.HostFacetSchema; } });
36
+ Object.defineProperty(exports, "TenantFacetSchema", { enumerable: true, get: function () { return schemas_1.TenantFacetSchema; } });
37
+ Object.defineProperty(exports, "ServiceFacetSchema", { enumerable: true, get: function () { return schemas_1.ServiceFacetSchema; } });
38
+ // ── Port-conflict detection ─────────────────────────────────────────────────
39
+ var ports_1 = require("./ports");
40
+ Object.defineProperty(exports, "detectPortConflicts", { enumerable: true, get: function () { return ports_1.detectPortConflicts; } });
41
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;AAkBH,iCAA+D;AAAtD,2GAAA,kBAAkB,OAAA;AAAE,yGAAA,gBAAgB,OAAA;AAE7C,+EAA+E;AAC/E,qCAYmB;AAXjB,0GAAA,eAAe,OAAA;AACf,4GAAA,iBAAiB,OAAA;AACjB,6GAAA,kBAAkB,OAAA;AAClB,0GAAA,eAAe,OAAA;AACf,6GAAA,kBAAkB,OAAA;AAClB,2GAAA,gBAAgB,OAAA;AAChB,4GAAA,iBAAiB,OAAA;AACjB,6GAAA,kBAAkB,OAAA;AAClB,0GAAA,eAAe,OAAA;AACf,4GAAA,iBAAiB,OAAA;AACjB,6GAAA,kBAAkB,OAAA;AAYpB,+EAA+E;AAC/E,iCAA8C;AAArC,4GAAA,mBAAmB,OAAA"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Port-conflict detection over the union of declared and observed bindings.
3
+ *
4
+ * The ownership split (§0) puts declared bindings in eury and observed ones in
5
+ * gaia. Neither side alone can see a collision. `detectPortConflicts` is the
6
+ * traversal over their union — so the collision returns as **rows, before the
7
+ * second tenant is provisioned**, instead of as a table a human reads after the
8
+ * fact (jgwill/gaia#75).
9
+ *
10
+ * A pure function over plain data. No I/O, no ontology traversal, no clock.
11
+ *
12
+ * Spec: /opt/eury/rispecs/foundations/02-specifications.md §S3.
13
+ */
14
+ import type { PortBinding, PortConflict } from './types';
15
+ /**
16
+ * Every port on a host claimed by more than one service.
17
+ *
18
+ * Two properties are load-bearing and are what the tests pin:
19
+ *
20
+ * 1. **The same service claiming the same slot twice is not a conflict.** The
21
+ * input is declared ∪ observed, so eury declaring `assembly-mux.service` on
22
+ * `:9081` and gaia observing it there produces two bindings for one claim.
23
+ * Claimants are de-duplicated by `boundBy`; only two *distinct* services
24
+ * collide.
25
+ * 2. **Ports are scarce per host, not globally.** `gmusic` on `:4444` on `eury`
26
+ * and something on `:4444` on `gaia` are not in tension.
27
+ *
28
+ * Bindings with no `proto` are read as {@link DEFAULT_PORT_PROTO}, so a tcp
29
+ * claim written longhand and one written shorthand still collide with
30
+ * each other, while tcp and udp on the same number do not.
31
+ *
32
+ * Output is deterministic — rows sorted by host, then proto, then port;
33
+ * claimants sorted within each row — so a caller can diff two runs.
34
+ */
35
+ export declare function detectPortConflicts(bindings: PortBinding[]): PortConflict[];
36
+ //# sourceMappingURL=ports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAa,MAAM,SAAS,CAAC;AAOpE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,YAAY,EAAE,CAmC3E"}
package/dist/ports.js ADDED
Binary file
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.js","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;AA8BH,kDAmCC;AA/DD,mCAA6C;AAG7C,4DAA4D;AAC5D,SAAS,OAAO,CAAC,IAAY,EAAE,KAAgB,EAAE,IAAY;IAC3D,OAAO,GAAG,IAAI,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;AACpC,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAgB,mBAAmB,CAAC,QAAuB;IACzD,MAAM,KAAK,GAAG,IAAI,GAAG,EAGlB,CAAC;IAEJ,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,0BAAkB,CAAC;QAClD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAEvD,IAAI,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;YAC/E,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACvB,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,SAAS,GAAmB,EAAE,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;QAClC,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC;YAAE,SAAS;QACtC,SAAS,CAAC,IAAI,CAAC;YACb,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,SAAS,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE;SACtC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,SAAS,CAAC,IAAI,CACnB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5B,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC;QAC9B,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAClB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Zod schemas for every facet type.
3
+ *
4
+ * These exist so an emitted manifest can be validated **at the boundary**
5
+ * between gaia (which emits observed facets read live from systemd) and eury
6
+ * (which authors desired-state edges) before it is trusted. A legible-but-stale
7
+ * model lies with a straight face; the parse is where that gets caught.
8
+ *
9
+ * Spec: /opt/eury/rispecs/foundations/02-specifications.md §S1.
10
+ */
11
+ import { z } from 'zod';
12
+ export declare const MetisHoldSchema: z.ZodObject<{
13
+ exceptions: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
14
+ invisibleWork: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
15
+ notes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
16
+ heldBy: z.ZodOptional<z.ZodString>;
17
+ }, "strip", z.ZodTypeAny, {
18
+ exceptions?: string[] | undefined;
19
+ invisibleWork?: string[] | undefined;
20
+ notes?: string[] | undefined;
21
+ heldBy?: string | undefined;
22
+ }, {
23
+ exceptions?: string[] | undefined;
24
+ invisibleWork?: string[] | undefined;
25
+ notes?: string[] | undefined;
26
+ heldBy?: string | undefined;
27
+ }>;
28
+ export declare const LingerStateSchema: z.ZodEnum<["enabled", "disabled", "unknown"]>;
29
+ export declare const ReachabilitySchema: z.ZodEnum<["lan", "tailnet", "cloudflare", "ngrok"]>;
30
+ export declare const PortProtoSchema: z.ZodEnum<["tcp", "udp"]>;
31
+ export declare const ServiceScopeSchema: z.ZodEnum<["user", "system"]>;
32
+ /**
33
+ * A port number as the kernel means it: an integer in 1–65535. Rejecting 0 and
34
+ * 65536 at the boundary is the point — an emitter that produces one has
35
+ * misparsed, and a misparsed binding silently never collides with anything.
36
+ */
37
+ export declare const PortNumberSchema: z.ZodNumber;
38
+ export declare const PortBindingSchema: z.ZodObject<{
39
+ port: z.ZodNumber;
40
+ proto: z.ZodOptional<z.ZodEnum<["tcp", "udp"]>>;
41
+ host: z.ZodString;
42
+ boundBy: z.ZodString;
43
+ }, "strip", z.ZodTypeAny, {
44
+ host: string;
45
+ port: number;
46
+ boundBy: string;
47
+ proto?: "tcp" | "udp" | undefined;
48
+ }, {
49
+ host: string;
50
+ port: number;
51
+ boundBy: string;
52
+ proto?: "tcp" | "udp" | undefined;
53
+ }>;
54
+ export declare const PortConflictSchema: z.ZodObject<{
55
+ host: z.ZodString;
56
+ port: z.ZodNumber;
57
+ proto: z.ZodEnum<["tcp", "udp"]>;
58
+ claimants: z.ZodArray<z.ZodString, "many">;
59
+ }, "strip", z.ZodTypeAny, {
60
+ host: string;
61
+ port: number;
62
+ proto: "tcp" | "udp";
63
+ claimants: string[];
64
+ }, {
65
+ host: string;
66
+ port: number;
67
+ proto: "tcp" | "udp";
68
+ claimants: string[];
69
+ }>;
70
+ export declare const HostFacetSchema: z.ZodObject<{
71
+ nodeId: z.ZodString;
72
+ hostname: z.ZodString;
73
+ fqdn: z.ZodOptional<z.ZodString>;
74
+ os: z.ZodOptional<z.ZodString>;
75
+ reachableVia: z.ZodOptional<z.ZodArray<z.ZodEnum<["lan", "tailnet", "cloudflare", "ngrok"]>, "many">>;
76
+ metis: z.ZodOptional<z.ZodObject<{
77
+ exceptions: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
78
+ invisibleWork: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
79
+ notes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
80
+ heldBy: z.ZodOptional<z.ZodString>;
81
+ }, "strip", z.ZodTypeAny, {
82
+ exceptions?: string[] | undefined;
83
+ invisibleWork?: string[] | undefined;
84
+ notes?: string[] | undefined;
85
+ heldBy?: string | undefined;
86
+ }, {
87
+ exceptions?: string[] | undefined;
88
+ invisibleWork?: string[] | undefined;
89
+ notes?: string[] | undefined;
90
+ heldBy?: string | undefined;
91
+ }>>;
92
+ }, "strip", z.ZodTypeAny, {
93
+ nodeId: string;
94
+ hostname: string;
95
+ fqdn?: string | undefined;
96
+ os?: string | undefined;
97
+ reachableVia?: ("lan" | "tailnet" | "cloudflare" | "ngrok")[] | undefined;
98
+ metis?: {
99
+ exceptions?: string[] | undefined;
100
+ invisibleWork?: string[] | undefined;
101
+ notes?: string[] | undefined;
102
+ heldBy?: string | undefined;
103
+ } | undefined;
104
+ }, {
105
+ nodeId: string;
106
+ hostname: string;
107
+ fqdn?: string | undefined;
108
+ os?: string | undefined;
109
+ reachableVia?: ("lan" | "tailnet" | "cloudflare" | "ngrok")[] | undefined;
110
+ metis?: {
111
+ exceptions?: string[] | undefined;
112
+ invisibleWork?: string[] | undefined;
113
+ notes?: string[] | undefined;
114
+ heldBy?: string | undefined;
115
+ } | undefined;
116
+ }>;
117
+ export declare const TenantFacetSchema: z.ZodObject<{
118
+ nodeId: z.ZodString;
119
+ account: z.ZodString;
120
+ uid: z.ZodOptional<z.ZodNumber>;
121
+ home: z.ZodOptional<z.ZodString>;
122
+ onHost: z.ZodString;
123
+ linger: z.ZodEnum<["enabled", "disabled", "unknown"]>;
124
+ metis: z.ZodOptional<z.ZodObject<{
125
+ exceptions: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
126
+ invisibleWork: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
127
+ notes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
128
+ heldBy: z.ZodOptional<z.ZodString>;
129
+ }, "strip", z.ZodTypeAny, {
130
+ exceptions?: string[] | undefined;
131
+ invisibleWork?: string[] | undefined;
132
+ notes?: string[] | undefined;
133
+ heldBy?: string | undefined;
134
+ }, {
135
+ exceptions?: string[] | undefined;
136
+ invisibleWork?: string[] | undefined;
137
+ notes?: string[] | undefined;
138
+ heldBy?: string | undefined;
139
+ }>>;
140
+ }, "strip", z.ZodTypeAny, {
141
+ nodeId: string;
142
+ account: string;
143
+ onHost: string;
144
+ linger: "enabled" | "disabled" | "unknown";
145
+ metis?: {
146
+ exceptions?: string[] | undefined;
147
+ invisibleWork?: string[] | undefined;
148
+ notes?: string[] | undefined;
149
+ heldBy?: string | undefined;
150
+ } | undefined;
151
+ uid?: number | undefined;
152
+ home?: string | undefined;
153
+ }, {
154
+ nodeId: string;
155
+ account: string;
156
+ onHost: string;
157
+ linger: "enabled" | "disabled" | "unknown";
158
+ metis?: {
159
+ exceptions?: string[] | undefined;
160
+ invisibleWork?: string[] | undefined;
161
+ notes?: string[] | undefined;
162
+ heldBy?: string | undefined;
163
+ } | undefined;
164
+ uid?: number | undefined;
165
+ home?: string | undefined;
166
+ }>;
167
+ export declare const ServiceFacetSchema: z.ZodObject<{
168
+ nodeId: z.ZodString;
169
+ unit: z.ZodString;
170
+ scope: z.ZodEnum<["user", "system"]>;
171
+ ownedBy: z.ZodString;
172
+ ports: z.ZodArray<z.ZodObject<{
173
+ port: z.ZodNumber;
174
+ proto: z.ZodOptional<z.ZodEnum<["tcp", "udp"]>>;
175
+ host: z.ZodString;
176
+ boundBy: z.ZodString;
177
+ }, "strip", z.ZodTypeAny, {
178
+ host: string;
179
+ port: number;
180
+ boundBy: string;
181
+ proto?: "tcp" | "udp" | undefined;
182
+ }, {
183
+ host: string;
184
+ port: number;
185
+ boundBy: string;
186
+ proto?: "tcp" | "udp" | undefined;
187
+ }>, "many">;
188
+ workingDirectory: z.ZodOptional<z.ZodString>;
189
+ execStop: z.ZodOptional<z.ZodString>;
190
+ metis: z.ZodOptional<z.ZodObject<{
191
+ exceptions: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
192
+ invisibleWork: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
193
+ notes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
194
+ heldBy: z.ZodOptional<z.ZodString>;
195
+ }, "strip", z.ZodTypeAny, {
196
+ exceptions?: string[] | undefined;
197
+ invisibleWork?: string[] | undefined;
198
+ notes?: string[] | undefined;
199
+ heldBy?: string | undefined;
200
+ }, {
201
+ exceptions?: string[] | undefined;
202
+ invisibleWork?: string[] | undefined;
203
+ notes?: string[] | undefined;
204
+ heldBy?: string | undefined;
205
+ }>>;
206
+ }, "strip", z.ZodTypeAny, {
207
+ nodeId: string;
208
+ unit: string;
209
+ scope: "user" | "system";
210
+ ownedBy: string;
211
+ ports: {
212
+ host: string;
213
+ port: number;
214
+ boundBy: string;
215
+ proto?: "tcp" | "udp" | undefined;
216
+ }[];
217
+ metis?: {
218
+ exceptions?: string[] | undefined;
219
+ invisibleWork?: string[] | undefined;
220
+ notes?: string[] | undefined;
221
+ heldBy?: string | undefined;
222
+ } | undefined;
223
+ workingDirectory?: string | undefined;
224
+ execStop?: string | undefined;
225
+ }, {
226
+ nodeId: string;
227
+ unit: string;
228
+ scope: "user" | "system";
229
+ ownedBy: string;
230
+ ports: {
231
+ host: string;
232
+ port: number;
233
+ boundBy: string;
234
+ proto?: "tcp" | "udp" | undefined;
235
+ }[];
236
+ metis?: {
237
+ exceptions?: string[] | undefined;
238
+ invisibleWork?: string[] | undefined;
239
+ notes?: string[] | undefined;
240
+ heldBy?: string | undefined;
241
+ } | undefined;
242
+ workingDirectory?: string | undefined;
243
+ execStop?: string | undefined;
244
+ }>;
245
+ export type ValidatedMetisHold = z.infer<typeof MetisHoldSchema>;
246
+ export type ValidatedPortBinding = z.infer<typeof PortBindingSchema>;
247
+ export type ValidatedPortConflict = z.infer<typeof PortConflictSchema>;
248
+ export type ValidatedHostFacet = z.infer<typeof HostFacetSchema>;
249
+ export type ValidatedTenantFacet = z.infer<typeof TenantFacetSchema>;
250
+ export type ValidatedServiceFacet = z.infer<typeof ServiceFacetSchema>;
251
+ //# sourceMappingURL=schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;EAK1B,CAAC;AAIH,eAAO,MAAM,iBAAiB,+CAA6C,CAAC;AAE5E,eAAO,MAAM,kBAAkB,sDAAoD,CAAC;AAEpF,eAAO,MAAM,eAAe,2BAAyB,CAAC;AAEtD,eAAO,MAAM,kBAAkB,+BAA6B,CAAC;AAI7D;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,aAAqC,CAAC;AAEnE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;EAK5B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;EAK7B,CAAC;AAIH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAO1B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQ5B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAS7B,CAAC;AAOH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AACjE,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AACrE,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACvE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AACjE,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AACrE,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ /**
3
+ * Zod schemas for every facet type.
4
+ *
5
+ * These exist so an emitted manifest can be validated **at the boundary**
6
+ * between gaia (which emits observed facets read live from systemd) and eury
7
+ * (which authors desired-state edges) before it is trusted. A legible-but-stale
8
+ * model lies with a straight face; the parse is where that gets caught.
9
+ *
10
+ * Spec: /opt/eury/rispecs/foundations/02-specifications.md §S1.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.ServiceFacetSchema = exports.TenantFacetSchema = exports.HostFacetSchema = exports.PortConflictSchema = exports.PortBindingSchema = exports.PortNumberSchema = exports.ServiceScopeSchema = exports.PortProtoSchema = exports.ReachabilitySchema = exports.LingerStateSchema = exports.MetisHoldSchema = void 0;
14
+ const zod_1 = require("zod");
15
+ // ── Métis (S5) ──────────────────────────────────────────────────────────────
16
+ exports.MetisHoldSchema = zod_1.z.object({
17
+ exceptions: zod_1.z.array(zod_1.z.string()).optional(),
18
+ invisibleWork: zod_1.z.array(zod_1.z.string()).optional(),
19
+ notes: zod_1.z.array(zod_1.z.string()).optional(),
20
+ heldBy: zod_1.z.string().optional(),
21
+ });
22
+ // ── Enumerations ────────────────────────────────────────────────────────────
23
+ exports.LingerStateSchema = zod_1.z.enum(['enabled', 'disabled', 'unknown']);
24
+ exports.ReachabilitySchema = zod_1.z.enum(['lan', 'tailnet', 'cloudflare', 'ngrok']);
25
+ exports.PortProtoSchema = zod_1.z.enum(['tcp', 'udp']);
26
+ exports.ServiceScopeSchema = zod_1.z.enum(['user', 'system']);
27
+ // ── Port bindings (S1, S3) ──────────────────────────────────────────────────
28
+ /**
29
+ * A port number as the kernel means it: an integer in 1–65535. Rejecting 0 and
30
+ * 65536 at the boundary is the point — an emitter that produces one has
31
+ * misparsed, and a misparsed binding silently never collides with anything.
32
+ */
33
+ exports.PortNumberSchema = zod_1.z.number().int().min(1).max(65535);
34
+ exports.PortBindingSchema = zod_1.z.object({
35
+ port: exports.PortNumberSchema,
36
+ proto: exports.PortProtoSchema.optional(),
37
+ host: zod_1.z.string().min(1),
38
+ boundBy: zod_1.z.string().min(1),
39
+ });
40
+ exports.PortConflictSchema = zod_1.z.object({
41
+ host: zod_1.z.string().min(1),
42
+ port: exports.PortNumberSchema,
43
+ proto: exports.PortProtoSchema,
44
+ claimants: zod_1.z.array(zod_1.z.string().min(1)).min(2),
45
+ });
46
+ // ── Facets (S1) ─────────────────────────────────────────────────────────────
47
+ exports.HostFacetSchema = zod_1.z.object({
48
+ nodeId: zod_1.z.string().min(1),
49
+ hostname: zod_1.z.string().min(1),
50
+ fqdn: zod_1.z.string().optional(),
51
+ os: zod_1.z.string().optional(),
52
+ reachableVia: zod_1.z.array(exports.ReachabilitySchema).optional(),
53
+ metis: exports.MetisHoldSchema.optional(),
54
+ });
55
+ exports.TenantFacetSchema = zod_1.z.object({
56
+ nodeId: zod_1.z.string().min(1),
57
+ account: zod_1.z.string().min(1),
58
+ uid: zod_1.z.number().int().nonnegative().optional(),
59
+ home: zod_1.z.string().optional(),
60
+ onHost: zod_1.z.string().min(1),
61
+ linger: exports.LingerStateSchema,
62
+ metis: exports.MetisHoldSchema.optional(),
63
+ });
64
+ exports.ServiceFacetSchema = zod_1.z.object({
65
+ nodeId: zod_1.z.string().min(1),
66
+ unit: zod_1.z.string().min(1),
67
+ scope: exports.ServiceScopeSchema,
68
+ ownedBy: zod_1.z.string().min(1),
69
+ ports: zod_1.z.array(exports.PortBindingSchema),
70
+ workingDirectory: zod_1.z.string().optional(),
71
+ execStop: zod_1.z.string().optional(),
72
+ metis: exports.MetisHoldSchema.optional(),
73
+ });
74
+ //# sourceMappingURL=schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAEH,6BAAwB;AAExB,+EAA+E;AAElE,QAAA,eAAe,GAAG,OAAC,CAAC,MAAM,CAAC;IACtC,UAAU,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC1C,aAAa,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC7C,KAAK,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACrC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC9B,CAAC,CAAC;AAEH,+EAA+E;AAElE,QAAA,iBAAiB,GAAG,OAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC;AAE/D,QAAA,kBAAkB,GAAG,OAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;AAEvE,QAAA,eAAe,GAAG,OAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AAEzC,QAAA,kBAAkB,GAAG,OAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;AAE7D,+EAA+E;AAE/E;;;;GAIG;AACU,QAAA,gBAAgB,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAEtD,QAAA,iBAAiB,GAAG,OAAC,CAAC,MAAM,CAAC;IACxC,IAAI,EAAE,wBAAgB;IACtB,KAAK,EAAE,uBAAe,CAAC,QAAQ,EAAE;IACjC,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC3B,CAAC,CAAC;AAEU,QAAA,kBAAkB,GAAG,OAAC,CAAC,MAAM,CAAC;IACzC,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,IAAI,EAAE,wBAAgB;IACtB,KAAK,EAAE,uBAAe;IACtB,SAAS,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;CAC7C,CAAC,CAAC;AAEH,+EAA+E;AAElE,QAAA,eAAe,GAAG,OAAC,CAAC,MAAM,CAAC;IACtC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACzB,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3B,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACzB,YAAY,EAAE,OAAC,CAAC,KAAK,CAAC,0BAAkB,CAAC,CAAC,QAAQ,EAAE;IACpD,KAAK,EAAE,uBAAe,CAAC,QAAQ,EAAE;CAClC,CAAC,CAAC;AAEU,QAAA,iBAAiB,GAAG,OAAC,CAAC,MAAM,CAAC;IACxC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACzB,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE;IAC9C,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACzB,MAAM,EAAE,yBAAiB;IACzB,KAAK,EAAE,uBAAe,CAAC,QAAQ,EAAE;CAClC,CAAC,CAAC;AAEU,QAAA,kBAAkB,GAAG,OAAC,CAAC,MAAM,CAAC;IACzC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACzB,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,KAAK,EAAE,0BAAkB;IACzB,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,KAAK,EAAE,OAAC,CAAC,KAAK,CAAC,yBAAiB,CAAC;IACjC,gBAAgB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACvC,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,KAAK,EAAE,uBAAe,CAAC,QAAQ,EAAE;CAClC,CAAC,CAAC"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Facet types for @medicine-wheel/infra.
3
+ *
4
+ * A facet **annotates an existing `RelationalNode` by id** — it never re-declares
5
+ * one. Each facet gives a typed home to the machine specifics (`port`, `unit`,
6
+ * `linger`) that today leak into `metadata: Record<string, unknown>`.
7
+ *
8
+ * A facet inherits `direction` from the node it annotates and never re-encodes it.
9
+ * `ontology-core`'s closed `NodeType` union stays untouched: infra entities reuse
10
+ * the existing values via {@link FACET_NODE_TYPES}.
11
+ *
12
+ * Spec: /opt/eury/rispecs/foundations/02-specifications.md §S1, §S5.
13
+ */
14
+ import type { RelationalNode } from '@medicine-wheel/ontology-core';
15
+ /** The id of the `RelationalNode` a facet annotates. */
16
+ export type NodeId = RelationalNode['id'];
17
+ /**
18
+ * The tacit operator knowledge a legibility grid must **hold** rather than
19
+ * flatten. The four-directions ontology is an imposed grid; a grid that becomes
20
+ * the only sanctioned way to describe a system erases the "you have to restart
21
+ * it twice" and the "don't remove the BSSID lock" that kept it alive.
22
+ *
23
+ * Optional on every facet, on principle — infrastructure studies exists to
24
+ * surface invisible work, not standardise it away.
25
+ */
26
+ export interface MetisHold {
27
+ /** 'restart twice; first start races the mount' */
28
+ exceptions?: string[];
29
+ /** 'BELL917 BSSID lock is load-bearing — do not remove' */
30
+ invisibleWork?: string[];
31
+ /** credential paths, ordering lore, the unwritten */
32
+ notes?: string[];
33
+ /** who carries this know-how — accountability, not anonymised */
34
+ heldBy?: string;
35
+ }
36
+ /**
37
+ * Whether a tenant's user units survive logout.
38
+ *
39
+ * A machine fact read at runtime — a checkbox with no authority and no ability
40
+ * to withdraw. It is deliberately **not** a consent state: the human
41
+ * authorization to run the root step that sets it is a `ConsentRecord` in
42
+ * `@medicine-wheel/consent-lifecycle`, and the two are never collapsed. The
43
+ * structured `Precondition` that references both arrives in `0.2.0` (§S4).
44
+ */
45
+ export type LingerState = 'enabled' | 'disabled' | 'unknown';
46
+ /** How a host can be reached. */
47
+ export type Reachability = 'lan' | 'tailnet' | 'cloudflare' | 'ngrok';
48
+ /** Transport of a port binding. Absent means {@link DEFAULT_PORT_PROTO}. */
49
+ export type PortProto = 'tcp' | 'udp';
50
+ /** systemd manager a unit belongs to. */
51
+ export type ServiceScope = 'user' | 'system';
52
+ /** The proto a `PortBinding` is read as when it declares none. */
53
+ export declare const DEFAULT_PORT_PROTO: PortProto;
54
+ /** A machine. Annotates a `RelationalNode` of type `'land'`. */
55
+ export interface HostFacet {
56
+ /** → `RelationalNode.id` (type: `'land'`) */
57
+ nodeId: NodeId;
58
+ /** 'gaia' | 'eury' */
59
+ hostname: string;
60
+ fqdn?: string;
61
+ os?: string;
62
+ reachableVia?: Reachability[];
63
+ metis?: MetisHold;
64
+ }
65
+ /**
66
+ * A unix account. Annotates a `RelationalNode` of type `'human'` — an account is
67
+ * a being, not a namespace.
68
+ */
69
+ export interface TenantFacet {
70
+ /** → `RelationalNode.id` (type: `'human'`) */
71
+ nodeId: NodeId;
72
+ /** unix user: 'gmusic' | 'ava' */
73
+ account: string;
74
+ uid?: number;
75
+ home?: string;
76
+ /** → `HostFacet.nodeId` */
77
+ onHost: NodeId;
78
+ /** consent *precondition*, not consent — see {@link LingerState} */
79
+ linger: LingerState;
80
+ metis?: MetisHold;
81
+ }
82
+ /**
83
+ * A systemd unit. Annotates a `RelationalNode` of type `'knowledge'` — West
84
+ * (the thing that executes) made typed.
85
+ */
86
+ export interface ServiceFacet {
87
+ /** → `RelationalNode.id` (type: `'knowledge'`) */
88
+ nodeId: NodeId;
89
+ /** 'assembly-mux.service' */
90
+ unit: string;
91
+ scope: ServiceScope;
92
+ /** → `TenantFacet.nodeId` */
93
+ ownedBy: NodeId;
94
+ ports: PortBinding[];
95
+ /** the docker-zulip `.env`-is-read-from-cwd finding (jgwill/gaia#74) */
96
+ workingDirectory?: string;
97
+ /** 'compose stop', never 'compose down' (jgwill/gaia#74) */
98
+ execStop?: string;
99
+ metis?: MetisHold;
100
+ }
101
+ /**
102
+ * A service's claim on a port. South, structurally: a precondition that must
103
+ * hold before West runs. Ports are scarce **per host**, which is why `host` is
104
+ * part of the identity of a binding and not an annotation on it.
105
+ */
106
+ export interface PortBinding {
107
+ port: number;
108
+ proto?: PortProto;
109
+ /** → `HostFacet.nodeId` */
110
+ host: NodeId;
111
+ /** → `ServiceFacet.nodeId` */
112
+ boundBy: NodeId;
113
+ }
114
+ /** One port on one host claimed by more than one service. */
115
+ export interface PortConflict {
116
+ /** → `HostFacet.nodeId` */
117
+ host: NodeId;
118
+ port: number;
119
+ /**
120
+ * The transport the claimants collide on.
121
+ *
122
+ * Addition to the §S3 shape, which names only `host`/`port`/`claimants`.
123
+ * Without it, a host colliding on both tcp/53 and udp/53 returns two rows
124
+ * that cannot be told apart. Always concrete — an absent
125
+ * `PortBinding.proto` is normalised to {@link DEFAULT_PORT_PROTO}.
126
+ */
127
+ proto: PortProto;
128
+ /** `ServiceFacet.nodeId[]` competing for it — distinct, sorted */
129
+ claimants: NodeId[];
130
+ }
131
+ /**
132
+ * Which closed `NodeType` each facet annotates.
133
+ *
134
+ * Typed against `ontology-core`'s union on purpose: if that union ever changes
135
+ * shape, `infra` fails to compile here rather than drifting silently. This is
136
+ * the whole reason the dependency is load-bearing rather than decorative — no
137
+ * `NodeType` is widened, the existing values are reused.
138
+ */
139
+ export declare const FACET_NODE_TYPES: {
140
+ readonly host: "land";
141
+ readonly tenant: "human";
142
+ readonly service: "knowledge";
143
+ };
144
+ /** `'host' | 'tenant' | 'service'` */
145
+ export type FacetKind = keyof typeof FACET_NODE_TYPES;
146
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ /**
3
+ * Facet types for @medicine-wheel/infra.
4
+ *
5
+ * A facet **annotates an existing `RelationalNode` by id** — it never re-declares
6
+ * one. Each facet gives a typed home to the machine specifics (`port`, `unit`,
7
+ * `linger`) that today leak into `metadata: Record<string, unknown>`.
8
+ *
9
+ * A facet inherits `direction` from the node it annotates and never re-encodes it.
10
+ * `ontology-core`'s closed `NodeType` union stays untouched: infra entities reuse
11
+ * the existing values via {@link FACET_NODE_TYPES}.
12
+ *
13
+ * Spec: /opt/eury/rispecs/foundations/02-specifications.md §S1, §S5.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.FACET_NODE_TYPES = exports.DEFAULT_PORT_PROTO = void 0;
17
+ /** The proto a `PortBinding` is read as when it declares none. */
18
+ exports.DEFAULT_PORT_PROTO = 'tcp';
19
+ // ── Node-type binding ───────────────────────────────────────────────────────
20
+ /**
21
+ * Which closed `NodeType` each facet annotates.
22
+ *
23
+ * Typed against `ontology-core`'s union on purpose: if that union ever changes
24
+ * shape, `infra` fails to compile here rather than drifting silently. This is
25
+ * the whole reason the dependency is load-bearing rather than decorative — no
26
+ * `NodeType` is widened, the existing values are reused.
27
+ */
28
+ exports.FACET_NODE_TYPES = {
29
+ host: 'land',
30
+ tenant: 'human',
31
+ service: 'knowledge',
32
+ };
33
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;AAmDH,kEAAkE;AACrD,QAAA,kBAAkB,GAAc,KAAK,CAAC;AAwFnD,+EAA+E;AAE/E;;;;;;;GAOG;AACU,QAAA,gBAAgB,GAAG;IAC9B,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,OAAO;IACf,OAAO,EAAE,WAAW;CACuB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@medicine-wheel/infra",
3
+ "version": "0.5.5",
4
+ "description": "Typed infrastructure facets for the Medicine Wheel Developer Suite — hosts, tenants, services, and port bindings as facets keyed by relational node id, with port-conflict detection over declared ∪ observed state",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js",
11
+ "require": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./types": {
15
+ "types": "./dist/types.d.ts",
16
+ "import": "./dist/types.js",
17
+ "require": "./dist/types.js",
18
+ "default": "./dist/types.js"
19
+ },
20
+ "./schemas": {
21
+ "types": "./dist/schemas.d.ts",
22
+ "import": "./dist/schemas.js",
23
+ "require": "./dist/schemas.js",
24
+ "default": "./dist/schemas.js"
25
+ }
26
+ },
27
+ "sideEffects": false,
28
+ "files": [
29
+ "dist",
30
+ "README.md"
31
+ ],
32
+ "scripts": {
33
+ "build": "tsc",
34
+ "dev": "tsc --watch",
35
+ "lint": "tsc --noEmit",
36
+ "clean": "rm -rf dist",
37
+ "prepublishOnly": "npm run clean && npm run build"
38
+ },
39
+ "keywords": [
40
+ "medicine-wheel",
41
+ "infrastructure",
42
+ "systemd",
43
+ "relational-ontology",
44
+ "port-conflict",
45
+ "topology-as-data",
46
+ "metis"
47
+ ],
48
+ "author": "IAIP Collaborative - Shawinigan, QC",
49
+ "license": "MIT",
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/jgwill/medicine-wheel.git",
53
+ "directory": "src/infra"
54
+ },
55
+ "dependencies": {
56
+ "@medicine-wheel/ontology-core": "^0.5.5",
57
+ "zod": "^3.23.0"
58
+ },
59
+ "devDependencies": {
60
+ "@types/node": "^20.10.0",
61
+ "typescript": "^5.3.0"
62
+ }
63
+ }