@openship/protocol 0.1.0 → 0.1.2

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 CHANGED
@@ -15,3 +15,5 @@ if (imported.snapshot.kind === "systems") validateSystems(imported.snapshot.docu
15
15
  The package contains the exact canonical `skills/openship` schemas, examples, and references. See the repository root README for the generated-skill workflow.
16
16
 
17
17
  Version 0.1.0 replaces legacy Systems with `systemsVersion: "2.0"`: `system.layers`, `system.refinements`, and optional `system.instances`. Sources and Changes retain their 1.0 formats. Consumers must migrate; `validateSystems` explicitly rejects the legacy graph. Each layer is independently renderable, while node IDs and shared context span the entire system.
18
+
19
+ Version 0.1.1 adds optional `system.domains` entries (`id`, `name`, `nodeIds`, optional `description`). Membership may overlap and span layers. Documents without domains remain valid; `openship: "1.0"` and `systemsVersion: "2.0"` do not change.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "openship": "1.0",
3
3
  "package": "@openship/protocol",
4
- "packageVersion": "0.1.0",
4
+ "packageVersion": "0.1.2",
5
5
  "source": "https://github.com/openshipdev/openship/tree/main/skills/openship",
6
- "sourceCommit": "f1338ebcb6a49927383b5b7d55be70a3d966d8d2"
6
+ "sourceCommit": "11f343e2e3418c0a2bb473d229099e4f9431f801"
7
7
  }
@@ -326,6 +326,27 @@
326
326
  }
327
327
  ]
328
328
  }
329
+ ],
330
+ "domains": [
331
+ {
332
+ "id": "web",
333
+ "name": "Web app",
334
+ "nodeIds": [
335
+ "logical.web",
336
+ "p.web",
337
+ "provider.web"
338
+ ]
339
+ },
340
+ {
341
+ "id": "state",
342
+ "name": "State",
343
+ "nodeIds": [
344
+ "logical.data",
345
+ "technical.data",
346
+ "provider.data",
347
+ "p.web"
348
+ ]
349
+ }
329
350
  ]
330
351
  },
331
352
  "systemsVersion": "2.0"
@@ -38,7 +38,11 @@ Any positive number of layers is supported. Each is an explicit graph, not a gen
38
38
 
39
39
  ## Nodes, containment and sources
40
40
 
41
- Node kinds are `Root`, `Block`, `Store`, `Host`, `Container`, `Process`, and `Library`. Block describes a capability; Store describes persistent data. The other kinds describe a system boundary, execution environment, grouped runtime, executable component, and reusable dependency respectively.
41
+ Node kinds are `Root`, `Block`, `Store`, `Host`, `Container`, `Process`, `Contract`, and `Library`. Block describes a capability; Store describes persistent data. Root, Host, Container, Process, and Library describe a system boundary, execution environment, grouped runtime, executable component, and reusable dependency respectively.
42
+
43
+ A **Contract** is a Process running on consensus: its execution and state transitions are governed by the consensus rules of its network. “Smart” is implied. Use `Contract` for consensus-executed components and `Process` for ordinary runtimes, including RPC servers and transaction relayers. Contract follows the same containment, connection, refinement, configuration, and instance-binding rules as Process; database `state` remains exclusive to Store bindings. The network can be described by a parent Host and configuration or metadata.
44
+
45
+ `Contract` extends the node-kind vocabulary without changing the `openship: "1.0"` or `systemsVersion: "2.0"` envelope. Consumers with the previous closed vocabulary must update before accepting documents containing Contract.
42
46
 
43
47
  ```json
44
48
  {
@@ -65,6 +69,23 @@ Layer-local edges have `id`, `type`, `fromNodeId`, `toNodeId`, and optional meta
65
69
 
66
70
  Refinements have `id`, `fromNodeId`, and `toNodeId`. IDs MUST be unique within `system.refinements`. The source MUST belong to a later layer than the target. Thus the concrete source implements the more abstract target. Many-to-many mappings, skipped layers, and root mappings are allowed. Refinements neither imply containment nor copy configuration, documents, or runtime edges.
67
71
 
72
+ ## Domains (optional)
73
+
74
+ `system.domains` MAY declare an ordered list of domains. A domain groups blocks independently of layers, containment, and refinement. Each requires a unique `id`, a nonempty `name`, and `nodeIds`; an optional nonempty `description` explains its scope. Domain IDs use the standard ID grammar. Member IDs MUST be unique within the domain and MUST reference existing nodes anywhere in the system.
75
+
76
+ ```json
77
+ "domains": [
78
+ { "id": "web", "name": "Web app", "nodeIds": ["logical.web", "technical.web"] },
79
+ { "id": "state", "name": "State", "nodeIds": ["technical.web", "technical.database"] }
80
+ ]
81
+ ```
82
+
83
+ A node MAY belong to zero, one, or multiple domains. Empty domains and an empty domain list are valid. Membership is explicit and is not inherited through parents or refinements. Omitting domains preserves the existing Systems behavior.
84
+
85
+ Viewers SHOULD select all domains initially. When filtering, a node matches if it has no domain or belongs to any selected domain. Keep the layer root and ancestors needed to render matching nodes as structural boundaries; this does not make other children visible. Hide connections whose endpoints do not match. Domain filters are presentation controls, not access controls. Keep domain selections across layer changes.
86
+
87
+ Domains are an additive capability in package 0.1.1. The envelope remains `openship: "1.0"` and `systemsVersion: "2.0"`.
88
+
68
89
  ## Configuration
69
90
 
70
91
  Nodes and instance bindings MAY contain a `configuration` array. Each entry requires a unique `name`, a nonempty `description`, and a boolean `required` flag. Optional `value` contains JSON data; its absence means unresolved, whereas an explicit null is a supplied value.
@@ -159,7 +180,7 @@ Artifact IDs are unique within the system.
159
180
  1. Envelope, Systems version, and embedded Sources integrity.
160
181
  2. Layer IDs, global node IDs, roots, containment, configuration and source selectors.
161
182
  3. Layer-local edges and cycle checks.
162
- 4. Refinement endpoints and order.
183
+ 4. Domain declarations and member references; refinement endpoints and order.
163
184
  5. Instance membership, bindings, configuration, state and secret scopes.
164
185
  6. Shared document hashes, concerns, matrix/artifact references, prompts and supersession chains.
165
186
 
@@ -77,7 +77,13 @@
77
77
  },
78
78
  "nodes": false,
79
79
  "edges": false,
80
- "rootNodeId": false
80
+ "rootNodeId": false,
81
+ "domains": {
82
+ "type": "array",
83
+ "items": {
84
+ "$ref": "#/$defs/domain"
85
+ }
86
+ }
81
87
  },
82
88
  "additionalProperties": true
83
89
  },
@@ -118,6 +124,7 @@
118
124
  "Host",
119
125
  "Container",
120
126
  "Process",
127
+ "Contract",
121
128
  "Library",
122
129
  "Block",
123
130
  "Store"
@@ -577,6 +584,35 @@
577
584
  }
578
585
  },
579
586
  "additionalProperties": true
587
+ },
588
+ "domain": {
589
+ "type": "object",
590
+ "required": [
591
+ "id",
592
+ "name",
593
+ "nodeIds"
594
+ ],
595
+ "properties": {
596
+ "id": {
597
+ "$ref": "#/$defs/id"
598
+ },
599
+ "name": {
600
+ "type": "string",
601
+ "minLength": 1
602
+ },
603
+ "description": {
604
+ "type": "string",
605
+ "minLength": 1
606
+ },
607
+ "nodeIds": {
608
+ "type": "array",
609
+ "items": {
610
+ "$ref": "#/$defs/id"
611
+ },
612
+ "uniqueItems": true
613
+ }
614
+ },
615
+ "additionalProperties": true
580
616
  }
581
617
  },
582
618
  "additionalProperties": true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openship/protocol",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Canonical OpenShip 1.0 types, validators, consumer helpers, schemas, and skill assets.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/index.d.ts CHANGED
@@ -10,7 +10,7 @@ export interface SourcesManifest { openship: "1.0"; capability: "sources"; diges
10
10
  export interface SourcesBundle { openship: "1.0"; capability: "sources"; digest: string; files: Record<string, { encoding: OpenShipEncoding; content: string; [key: string]: unknown }>; [key: string]: unknown }
11
11
  export interface DiscoveryAgent { summary: string; instructions: string; skill: string; [key: string]: unknown }
12
12
  export interface DiscoveryDocument { openship: "1.0"; capability: "discovery"; project: { name: string; description: string; [key: string]: unknown }; agent: DiscoveryAgent; page?: string; capabilities: { sources: { description: string; manifest: string; bundle: string; mcp?: string; [key: string]: unknown }; systems?: { description: string; document: string; [key: string]: unknown }; changes?: { description: string; policy: string; submit: string; status: string; [key: string]: unknown }; [key: string]: unknown }; [key: string]: unknown }
13
- export type SystemsNodeKind = "Root" | "Block" | "Store" | "Host" | "Container" | "Process" | "Library";
13
+ export type SystemsNodeKind = "Root" | "Block" | "Store" | "Host" | "Container" | "Process" | "Contract" | "Library";
14
14
  export type SystemsNodeOwnership = "first_party" | "third_party";
15
15
  export type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };
16
16
  export interface SystemsConfiguration { name: string; description: string; required: boolean; sensitive?: boolean; value?: JsonValue; secretRef?: { nodeId: string; key: string }; }
@@ -21,7 +21,8 @@ export interface SystemsLayer { id: string; name: string; role: "logical" | "tec
21
21
  export interface SystemsRefinement { id: string; fromNodeId: string; toNodeId: string; [key: string]: unknown }
22
22
  export interface SystemsBinding { nodeId: string; resourceId?: string; configuration?: SystemsConfiguration[]; state?: { appliedMigration?: string; snapshot?: { ref: string; capturedAt: string; digest?: string } }; [key: string]: unknown }
23
23
  export interface SystemsInstance { id: string; name: string; environment: string; layerId: string; bindings: SystemsBinding[]; [key: string]: unknown }
24
- export interface SystemsGraph { id: string; name: string; layers: SystemsLayer[]; refinements: SystemsRefinement[]; instances?: SystemsInstance[]; metadata?: Record<string, unknown>; context?: Record<string, unknown>; [key: string]: unknown }
24
+ export interface SystemsDomain { id: string; name: string; description?: string; nodeIds: string[]; [key: string]: unknown }
25
+ export interface SystemsGraph { id: string; name: string; layers: SystemsLayer[]; refinements: SystemsRefinement[]; domains?: SystemsDomain[]; instances?: SystemsInstance[]; metadata?: Record<string, unknown>; context?: Record<string, unknown>; [key: string]: unknown }
25
26
  export interface SystemsDocument { openship: "1.0"; capability: "systems"; systemsVersion: "2.0"; source: { manifest: SourcesManifest; bundle: SourcesBundle; [key: string]: unknown }; system: SystemsGraph; [key: string]: unknown }
26
27
  export interface VerifiedSourceFile { metadata: SourceFileMetadata; bytes: Uint8Array }
27
28
  export interface VerifiedSources { manifest: SourcesManifest; bundle: SourcesBundle; files: VerifiedSourceFile[]; decodedBytes: number }
package/src/index.js CHANGED
@@ -299,7 +299,7 @@ export function validateSystems(value, options = {}) {
299
299
  const path = `${at}.nodes[${i}]`, node = object(rawNode, path);
300
300
  identifier(node.id, `${path}.id`);
301
301
  if (nodeById.has(node.id)) fail(`${path}.id`, "must be globally unique");
302
- if (!["Root", "Block", "Store", "Host", "Container", "Process", "Library"].includes(node.kind)) fail(`${path}.kind`, "invalid node kind");
302
+ if (!["Root", "Block", "Store", "Host", "Container", "Process", "Contract", "Library"].includes(node.kind)) fail(`${path}.kind`, "invalid node kind");
303
303
  string(node.name, `${path}.name`);
304
304
  const metadata = object(node.metadata, `${path}.metadata`);
305
305
  if (!["first_party", "third_party"].includes(metadata.ownership)) fail(`${path}.metadata.ownership`, "must be first_party or third_party");
@@ -326,6 +326,16 @@ export function validateSystems(value, options = {}) {
326
326
  }
327
327
  for (const type of ["Dataflow", "Dependency"]) assertAcyclic([...local.keys()], edges.filter((edge) => edge.type === type).map((edge) => [edge.fromNodeId, edge.toNodeId]), `${at}.edges[${type}]`);
328
328
  }
329
+ const domains = array(system.domains === undefined ? [] : system.domains, "$.system.domains");
330
+ unique(domains.map((domain) => identifier(object(domain, "$.system.domains").id, "$.system.domains.id")), "$.system.domains");
331
+ for (const domain of domains) {
332
+ const at = `$.system.domains.${domain.id}`;
333
+ string(domain.name, `${at}.name`);
334
+ if (domain.description !== undefined) string(domain.description, `${at}.description`);
335
+ const members = array(domain.nodeIds, `${at}.nodeIds`);
336
+ unique(members, `${at}.nodeIds`);
337
+ for (const nodeId of members) if (!nodeById.has(nodeId)) fail(`${at}.nodeIds`, "must reference existing system nodes");
338
+ }
329
339
  const refinements = array(system.refinements, "$.system.refinements");
330
340
  unique(refinements.map((ref) => identifier(object(ref, "$.system.refinements").id, "$.system.refinements.id")), "$.system.refinements");
331
341
  for (const ref of refinements) {