@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 +2 -0
- package/dist/package-meta.json +2 -2
- package/dist/skill/references/examples/valid/systems-layered.json +21 -0
- package/dist/skill/references/openship-systems.md +23 -2
- package/dist/skill/references/schemas/systems.schema.json +37 -1
- package/package.json +1 -1
- package/src/index.d.ts +3 -2
- package/src/index.js +11 -1
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.
|
package/dist/package-meta.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"openship": "1.0",
|
|
3
3
|
"package": "@openship/protocol",
|
|
4
|
-
"packageVersion": "0.1.
|
|
4
|
+
"packageVersion": "0.1.2",
|
|
5
5
|
"source": "https://github.com/openshipdev/openship/tree/main/skills/openship",
|
|
6
|
-
"sourceCommit": "
|
|
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.
|
|
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.
|
|
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
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
|
|
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) {
|