@openship/protocol 0.0.1
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 +12 -0
- package/bin/openship.mjs +96 -0
- package/dist/package-meta.json +7 -0
- package/dist/skill/SKILL.md +18 -0
- package/dist/skill/references/examples/invalid/changes-submission.json +9 -0
- package/dist/skill/references/examples/invalid/changes-violation.json +7 -0
- package/dist/skill/references/examples/invalid/discovery.json +13 -0
- package/dist/skill/references/examples/invalid/sources-manifest.json +11 -0
- package/dist/skill/references/examples/invalid/systems-ownership.json +29 -0
- package/dist/skill/references/examples/invalid/systems.json +34 -0
- package/dist/skill/references/examples/valid/changes-accepted.json +13 -0
- package/dist/skill/references/examples/valid/changes-policy.json +12 -0
- package/dist/skill/references/examples/valid/changes-status.json +12 -0
- package/dist/skill/references/examples/valid/changes-submission.json +11 -0
- package/dist/skill/references/examples/valid/changes-violation.json +16 -0
- package/dist/skill/references/examples/valid/discovery.json +20 -0
- package/dist/skill/references/examples/valid/sources-bundle.json +10 -0
- package/dist/skill/references/examples/valid/sources-manifest.json +27 -0
- package/dist/skill/references/examples/valid/systems.json +55 -0
- package/dist/skill/references/openship-changes.md +135 -0
- package/dist/skill/references/openship-sources.md +143 -0
- package/dist/skill/references/openship-systems.md +165 -0
- package/dist/skill/references/openship.md +125 -0
- package/dist/skill/references/schemas/changes-accepted.schema.json +17 -0
- package/dist/skill/references/schemas/changes-policy.schema.json +44 -0
- package/dist/skill/references/schemas/changes-status.schema.json +22 -0
- package/dist/skill/references/schemas/changes-submission.schema.json +34 -0
- package/dist/skill/references/schemas/changes-violation.schema.json +32 -0
- package/dist/skill/references/schemas/discovery.schema.json +61 -0
- package/dist/skill/references/schemas/sources-bundle.schema.json +27 -0
- package/dist/skill/references/schemas/sources-manifest.schema.json +79 -0
- package/dist/skill/references/schemas/systems.schema.json +121 -0
- package/package.json +52 -0
- package/src/index.d.ts +36 -0
- package/src/index.js +454 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# OpenShip Sources v1
|
|
2
|
+
|
|
3
|
+
OpenShip Sources lets a running project publish the exact source snapshot that produced it. A client starting with only the origin can retrieve, verify, and materialize that snapshot without a repository provider or version-control history.
|
|
4
|
+
|
|
5
|
+
Read [openship.md](openship.md) first for discovery, transport, and caching rules.
|
|
6
|
+
|
|
7
|
+
## Endpoints
|
|
8
|
+
|
|
9
|
+
Discovery advertises two required and three optional URLs:
|
|
10
|
+
|
|
11
|
+
| Link | Required | Meaning |
|
|
12
|
+
|---|---:|---|
|
|
13
|
+
| `manifest` | Yes | Project identity and file metadata, without content. |
|
|
14
|
+
| `bundle` | Yes | Every file's encoded content in one JSON document. |
|
|
15
|
+
| `file` | No | Raw content for one manifest path. |
|
|
16
|
+
| `archive` | No | The manifest file set as a compressed archive. |
|
|
17
|
+
| `instructions` | No | Plain-language project-specific retrieval guidance. |
|
|
18
|
+
|
|
19
|
+
All reads are public, CORS-readable GET requests.
|
|
20
|
+
|
|
21
|
+
## Manifest
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"openship": "1.0",
|
|
26
|
+
"capability": "sources",
|
|
27
|
+
"generatedAt": "2026-08-21T12:00:00.000Z",
|
|
28
|
+
"digest": "sha256:1c413f...",
|
|
29
|
+
"project": {
|
|
30
|
+
"name": "Example",
|
|
31
|
+
"description": "An example project.",
|
|
32
|
+
"homepage": "https://example.com",
|
|
33
|
+
"repository": "https://github.com/example/example",
|
|
34
|
+
"license": "MIT"
|
|
35
|
+
},
|
|
36
|
+
"totals": { "files": 2, "bytes": 42 },
|
|
37
|
+
"files": [
|
|
38
|
+
{
|
|
39
|
+
"path": "app/page.tsx",
|
|
40
|
+
"size": 30,
|
|
41
|
+
"sha256": "0f1e2d...",
|
|
42
|
+
"encoding": "utf-8",
|
|
43
|
+
"mediaType": "text/plain; charset=utf-8",
|
|
44
|
+
"type": "file"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Required members are `openship`, `capability`, `digest`, `project`, `totals`, and `files`. Project name and description are required. Repository, commit, stack, structure, setup, runtime, environment-variable names, ignore rules, generation time, lineage, and other metadata are optional.
|
|
51
|
+
|
|
52
|
+
See [schemas/sources-manifest.schema.json](schemas/sources-manifest.schema.json).
|
|
53
|
+
|
|
54
|
+
### File entries
|
|
55
|
+
|
|
56
|
+
Every entry MUST contain:
|
|
57
|
+
|
|
58
|
+
- `path`: a repository-relative NFC string using `/` separators.
|
|
59
|
+
- `size`: the raw byte length.
|
|
60
|
+
- `sha256`: 64 lowercase hexadecimal characters over the raw bytes.
|
|
61
|
+
- `encoding`: `utf-8` or `base64` for JSON transport.
|
|
62
|
+
- `mediaType`: the media type used by a file endpoint.
|
|
63
|
+
- `type`: `file` or `symlink`.
|
|
64
|
+
|
|
65
|
+
Paths MUST NOT begin with `/`, contain `\`, contain an empty, `.` or `..` segment, contain a NUL byte, or exceed 512 UTF-8 bytes. Paths MUST be unique and sorted by ascending UTF-8 bytes.
|
|
66
|
+
|
|
67
|
+
A symlink MUST include its repository-relative `target`. Its size and SHA-256 describe the bytes served by Bundle and File after resolving the declared target. An archive MAY preserve the link itself.
|
|
68
|
+
|
|
69
|
+
### Optional metadata
|
|
70
|
+
|
|
71
|
+
An `env` array contains environment-variable names only. It MUST NOT contain values. Commit metadata is informational: if a working tree was dirty, `commit.dirty` MUST be true and clients MUST NOT treat the commit SHA as the snapshot identity. The Sources digest is authoritative.
|
|
72
|
+
|
|
73
|
+
## Bundle
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"openship": "1.0",
|
|
78
|
+
"capability": "sources",
|
|
79
|
+
"digest": "sha256:1c413f...",
|
|
80
|
+
"files": {
|
|
81
|
+
"app/page.tsx": {
|
|
82
|
+
"encoding": "utf-8",
|
|
83
|
+
"content": "export default function Page() {}\n"
|
|
84
|
+
},
|
|
85
|
+
"public/logo.png": {
|
|
86
|
+
"encoding": "base64",
|
|
87
|
+
"content": "iVBORw0KGgo..."
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The Bundle `digest` MUST equal the Manifest digest. Its file keys MUST exactly equal the Manifest paths. Each entry's encoding MUST match its Manifest entry.
|
|
94
|
+
|
|
95
|
+
UTF-8 content MUST survive an encode/decode round trip. Base64 MUST use the standard alphabet and decode canonically; padded and unpadded input MAY be accepted, but producers SHOULD emit padded base64.
|
|
96
|
+
|
|
97
|
+
See [schemas/sources-bundle.schema.json](schemas/sources-bundle.schema.json).
|
|
98
|
+
|
|
99
|
+
## Digest
|
|
100
|
+
|
|
101
|
+
Compute the snapshot digest from Manifest file entries sorted by the UTF-8 bytes of `path`:
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
digest = "sha256:" + SHA256(
|
|
105
|
+
concat(path + "\0" + sha256_hex + "\n")
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`sha256_hex` is the lowercase, unprefixed file hash. Size, encoding, media type, file type, target, project metadata, and generation time do not enter the digest.
|
|
110
|
+
|
|
111
|
+
Two conforming snapshots with the same digest contain the same paths and raw bytes. The digest does not claim that unlisted files do not exist on the server; publication safety comes from the declared file-set requirement.
|
|
112
|
+
|
|
113
|
+
## Declared file set
|
|
114
|
+
|
|
115
|
+
A producer MUST derive the published set from a fail-closed declaration. A checked-in manifest, a version-control index, or another explicit allowlist is acceptable. An unconstrained directory walk is not.
|
|
116
|
+
|
|
117
|
+
The protocol does not mandate a checked-in `openship.json`. A project MAY use one as its implementation source of truth. Generated payload files that describe the snapshot MAY be excluded from the snapshot to avoid self-reference.
|
|
118
|
+
|
|
119
|
+
Regardless of the declaration, a producer MUST refuse secret-shaped paths such as private environment files, credentials, private keys, dependency caches, and version-control internals.
|
|
120
|
+
|
|
121
|
+
## File and archive retrieval
|
|
122
|
+
|
|
123
|
+
A File endpoint MUST compare the requested path against the Manifest as an exact string. It MUST NOT resolve an arbitrary URL path against a filesystem. Unknown paths return `404`.
|
|
124
|
+
|
|
125
|
+
An Archive MUST contain exactly the Manifest path set. It SHOULD preserve declared symlinks and file modes where practical. Archive bytes do not affect the Sources digest.
|
|
126
|
+
|
|
127
|
+
## Consumer verification
|
|
128
|
+
|
|
129
|
+
A conforming consumer:
|
|
130
|
+
|
|
131
|
+
1. Fetches discovery, Manifest, and Bundle.
|
|
132
|
+
2. Validates both JSON documents.
|
|
133
|
+
3. Confirms sorted unique safe paths and exact Manifest/Bundle key equality.
|
|
134
|
+
4. Decodes every file and verifies byte size and SHA-256.
|
|
135
|
+
5. Recomputes and compares the snapshot digest.
|
|
136
|
+
6. Writes only verified paths beneath a chosen empty destination.
|
|
137
|
+
|
|
138
|
+
A consumer MUST finish validation before executing any retrieved code.
|
|
139
|
+
|
|
140
|
+
## Security
|
|
141
|
+
|
|
142
|
+
Serving Sources makes every included byte public. Producers MUST NOT publish secrets, environment values, signing keys, private deployment configuration, or data exports. File selection is the primary security boundary; filters and pattern scans are defense in depth.
|
|
143
|
+
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# OpenShip Systems v1
|
|
2
|
+
|
|
3
|
+
OpenShip Systems is the advanced OpenShip capability. It publishes one self-contained JSON document containing a complete Sources snapshot, a typed architecture graph, and optional context for humans and agents.
|
|
4
|
+
|
|
5
|
+
Systems is JSON-only in v1. Legacy directory or YAML bundles are not canonical OpenShip Systems representations.
|
|
6
|
+
|
|
7
|
+
## Top-level document
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"openship": "1.0",
|
|
12
|
+
"capability": "systems",
|
|
13
|
+
"source": {
|
|
14
|
+
"manifest": { "openship": "1.0", "capability": "sources", "digest": "sha256:..." },
|
|
15
|
+
"bundle": { "openship": "1.0", "capability": "sources", "digest": "sha256:...", "files": {} }
|
|
16
|
+
},
|
|
17
|
+
"system": {
|
|
18
|
+
"id": "example-system",
|
|
19
|
+
"name": "Example system",
|
|
20
|
+
"rootNodeId": "s.root",
|
|
21
|
+
"nodes": [],
|
|
22
|
+
"edges": []
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`source.manifest` and `source.bundle` MUST form a valid, complete Sources snapshot. External URLs or omitted contents are not self-contained and are non-conformant.
|
|
28
|
+
|
|
29
|
+
See [schemas/systems.schema.json](schemas/systems.schema.json).
|
|
30
|
+
|
|
31
|
+
## Nodes and containment
|
|
32
|
+
|
|
33
|
+
Node kinds are closed in v1:
|
|
34
|
+
|
|
35
|
+
| Kind | Meaning |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `Root` | The logical boundary of the described system. |
|
|
38
|
+
| `Host` | An execution environment or external platform. |
|
|
39
|
+
| `Container` | A grouped runtime unit hosted by a Host. |
|
|
40
|
+
| `Process` | A running service or executable component. |
|
|
41
|
+
| `Library` | A reusable code dependency outside runtime containment. |
|
|
42
|
+
|
|
43
|
+
Canonical node shape:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"id": "p.api",
|
|
48
|
+
"kind": "Process",
|
|
49
|
+
"name": "API",
|
|
50
|
+
"parentId": "h.runtime",
|
|
51
|
+
"sourceSelectors": ["apps/api/**", "packages/contracts/index.ts"],
|
|
52
|
+
"metadata": { "runtime": "node", "ownership": "first_party" }
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Rules:
|
|
57
|
+
|
|
58
|
+
- Every node has `metadata.ownership`, whose value is `first_party` or `third_party`.
|
|
59
|
+
- `first_party` means the system publisher owns or controls the component's implementation or operation. `third_party` means an external provider owns or controls it.
|
|
60
|
+
- Exactly one node has kind `Root`; its ID equals `rootNodeId` and it has no parent.
|
|
61
|
+
- Every Host has the Root as parent.
|
|
62
|
+
- Every Container has a Host parent.
|
|
63
|
+
- Every Process has a Host or Container parent.
|
|
64
|
+
- Library has no parent. Process and Library cannot contain children.
|
|
65
|
+
- IDs are unique and match `^[A-Za-z0-9._:-]+$`.
|
|
66
|
+
- The containment graph is acyclic.
|
|
67
|
+
- Metadata is open-ended beyond the required, typed `ownership` member. Boundary, ID prefixes, host naming, and other metadata are not required by v1.
|
|
68
|
+
|
|
69
|
+
### Source selectors
|
|
70
|
+
|
|
71
|
+
`sourceSelectors` is optional. An exact path selects itself. A selector ending in `/**` selects that directory and descendants. No other wildcard syntax is defined.
|
|
72
|
+
|
|
73
|
+
Every selector MUST match at least one Manifest path. Multiple nodes MAY select the same path and some source paths MAY remain unassigned.
|
|
74
|
+
|
|
75
|
+
## Edges
|
|
76
|
+
|
|
77
|
+
Edge types are closed in v1:
|
|
78
|
+
|
|
79
|
+
| Type | Source | Target | Cycle rule |
|
|
80
|
+
|---|---|---|---|
|
|
81
|
+
| `Runtime` | Process | Process or Container | Cycles allowed. |
|
|
82
|
+
| `Dataflow` | Process | Process or Container | Projected graph must be acyclic. |
|
|
83
|
+
| `Dependency` | Process | Library | Graph must be acyclic. |
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"id": "e.api.database",
|
|
88
|
+
"type": "Runtime",
|
|
89
|
+
"fromNodeId": "p.api",
|
|
90
|
+
"toNodeId": "p.database",
|
|
91
|
+
"metadata": { "protocol": "pgwire", "layer7": "postgresql-sql" }
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Root and Host cannot be edge endpoints under these rules. A Container target represents an opaque routing boundary.
|
|
96
|
+
|
|
97
|
+
## Optional context
|
|
98
|
+
|
|
99
|
+
`system.context` MAY contain concerns, shared documents, matrix assignments, system prompts, and node-local artifacts. Omitting context does not reduce graph conformance.
|
|
100
|
+
|
|
101
|
+
### Concerns
|
|
102
|
+
|
|
103
|
+
Concerns are explicitly declared. The recommended interoperable vocabulary is:
|
|
104
|
+
|
|
105
|
+
1. `Features`
|
|
106
|
+
2. `General Specs`
|
|
107
|
+
3. `General Skills`
|
|
108
|
+
4. `Data Model`
|
|
109
|
+
5. `Interfaces`
|
|
110
|
+
6. `Connectivity`
|
|
111
|
+
7. `Security`
|
|
112
|
+
8. `Implementation`
|
|
113
|
+
9. `Deployment`
|
|
114
|
+
|
|
115
|
+
Projects MAY use a subset and MAY declare additional concerns. Names are case-sensitive.
|
|
116
|
+
|
|
117
|
+
### Documents, skills, and prompts
|
|
118
|
+
|
|
119
|
+
Shared input kinds are `Document`, `Skill`, and `Prompt`:
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"kind": "Document",
|
|
124
|
+
"hash": "sha256:...",
|
|
125
|
+
"title": "Public API contract",
|
|
126
|
+
"language": "en",
|
|
127
|
+
"text": "...",
|
|
128
|
+
"supersedes": "sha256:..."
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Compute the hash from UTF-8 bytes of:
|
|
133
|
+
|
|
134
|
+
```text
|
|
135
|
+
kind + "\n" + title + "\n" + language + "\n" + text
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`supersedes` is excluded. Supersession chains MUST be acyclic. Missing predecessors are allowed so a snapshot need not contain its entire history.
|
|
139
|
+
|
|
140
|
+
Matrix assignments connect a node, a declared concern, and one or more `Document` or `Skill` hashes. References MUST resolve to documents of the corresponding kind.
|
|
141
|
+
|
|
142
|
+
Prompts are not ordinary matrix references. `systemPromptRefs` belongs to context, references only `Prompt` documents, and applies only to the Root.
|
|
143
|
+
|
|
144
|
+
### Artifacts
|
|
145
|
+
|
|
146
|
+
Artifact types are `Summary`, `Docs`, and `Code`. Each belongs to one node and one declared concern.
|
|
147
|
+
|
|
148
|
+
- Summary and Docs carry UTF-8 `text` and optional language.
|
|
149
|
+
- Code carries `sourcePaths` that resolve to Manifest files.
|
|
150
|
+
- Code MUST NOT duplicate file contents already present in the embedded Bundle.
|
|
151
|
+
|
|
152
|
+
Artifact IDs are unique within the system.
|
|
153
|
+
|
|
154
|
+
## Validation order
|
|
155
|
+
|
|
156
|
+
A consumer SHOULD validate in this order:
|
|
157
|
+
|
|
158
|
+
1. Top-level schema and embedded Sources.
|
|
159
|
+
2. Node IDs, root, containment, and parent kinds.
|
|
160
|
+
3. Edge endpoints and cycle rules.
|
|
161
|
+
4. Source selectors.
|
|
162
|
+
5. Concern declarations and document hashes.
|
|
163
|
+
6. Matrix, prompt, artifact, and supersession references.
|
|
164
|
+
|
|
165
|
+
Unknown metadata keys MUST be preserved. Unknown node, edge, document, or artifact kinds are invalid in v1.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# OpenShip v1
|
|
2
|
+
|
|
3
|
+
Status: Draft v1
|
|
4
|
+
Protocol version: `1.0`
|
|
5
|
+
|
|
6
|
+
OpenShip is a public interface between a running project and the people or agents that want to understand, reproduce, or improve it. It has three capabilities:
|
|
7
|
+
|
|
8
|
+
1. **Sources** publishes an integrity-checked source snapshot.
|
|
9
|
+
2. **Changes** accepts a patch against a Sources digest and produces an isolated candidate origin.
|
|
10
|
+
3. **Systems** publishes a self-contained JSON description of source, architecture, infrastructure, and optional agent context.
|
|
11
|
+
|
|
12
|
+
Sources is the foundation. Changes depends on Sources. Systems embeds a complete Sources snapshot but does not require Changes.
|
|
13
|
+
|
|
14
|
+
The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are normative.
|
|
15
|
+
|
|
16
|
+
## Design goals
|
|
17
|
+
|
|
18
|
+
- Start from an origin, not from a repository provider.
|
|
19
|
+
- Use plain HTTP and JSON that small clients can implement.
|
|
20
|
+
- Make source identity independently verifiable.
|
|
21
|
+
- Keep public reading separate from permission to change or deploy.
|
|
22
|
+
- Let simple projects stop at Sources while advanced projects add Systems.
|
|
23
|
+
|
|
24
|
+
OpenShip does not prove who owns an origin or authored a source tree. A digest proves that two snapshots contain the same bytes. Identity and signed provenance may be layered on separately.
|
|
25
|
+
|
|
26
|
+
## Common envelope
|
|
27
|
+
|
|
28
|
+
Every OpenShip JSON document MUST contain:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"openship": "1.0",
|
|
33
|
+
"capability": "discovery"
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`capability` is one of `discovery`, `sources`, `changes`, or `systems`. A Changes error or status document remains capability `changes`.
|
|
38
|
+
|
|
39
|
+
Within major version 1, producers MAY add members without changing existing meanings. Consumers MUST ignore unknown members they do not need and SHOULD preserve them when transforming a document.
|
|
40
|
+
|
|
41
|
+
## Public discovery
|
|
42
|
+
|
|
43
|
+
An OpenShip origin MUST serve:
|
|
44
|
+
|
|
45
|
+
```text
|
|
46
|
+
GET /.well-known/openship.json
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The response MUST be unauthenticated JSON, MUST allow cross-origin reads with `Access-Control-Allow-Origin: *`, and MUST contain absolute HTTPS URLs outside local development.
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"openship": "1.0",
|
|
54
|
+
"capability": "discovery",
|
|
55
|
+
"project": {
|
|
56
|
+
"name": "Example",
|
|
57
|
+
"description": "A short description of the running project."
|
|
58
|
+
},
|
|
59
|
+
"skill": "https://example.com/openship/file/skills/openship/SKILL.md",
|
|
60
|
+
"capabilities": {
|
|
61
|
+
"sources": {
|
|
62
|
+
"manifest": "https://example.com/openship/manifest.json",
|
|
63
|
+
"bundle": "https://example.com/openship/bundle.json",
|
|
64
|
+
"file": "https://example.com/openship/file/{path}",
|
|
65
|
+
"archive": "https://example.com/openship/source.tar.gz",
|
|
66
|
+
"instructions": "https://example.com/openship/agent.txt"
|
|
67
|
+
},
|
|
68
|
+
"changes": {
|
|
69
|
+
"policy": "https://example.com/openship/policy.json",
|
|
70
|
+
"submit": "https://example.com/openship/changes",
|
|
71
|
+
"status": "https://example.com/openship/changes/{changeId}"
|
|
72
|
+
},
|
|
73
|
+
"systems": {
|
|
74
|
+
"document": "https://example.com/openship/system.json"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Requirements:
|
|
81
|
+
|
|
82
|
+
- `project`, `capabilities`, and `capabilities.sources` are REQUIRED.
|
|
83
|
+
- `project.name` and `project.description` are REQUIRED.
|
|
84
|
+
- `sources.manifest` and `sources.bundle` are REQUIRED.
|
|
85
|
+
- `changes`, `systems`, and `skill` are OPTIONAL and MUST be omitted when unavailable.
|
|
86
|
+
- Changes MUST NOT be advertised without Sources.
|
|
87
|
+
- `file` and `status` are URI templates with exactly the named expansion.
|
|
88
|
+
- A project implementing Sources and Changes but not Systems simply omits `capabilities.systems`.
|
|
89
|
+
|
|
90
|
+
See [schemas/discovery.schema.json](schemas/discovery.schema.json).
|
|
91
|
+
|
|
92
|
+
## Access and transport
|
|
93
|
+
|
|
94
|
+
Discovery, Sources, and Systems reads MUST NOT require cookies, credentials, custom headers, or query parameters. Changes writes MAY require authorization or payment disclosed by the Changes policy.
|
|
95
|
+
|
|
96
|
+
JSON responses MUST use UTF-8. Public reads SHOULD support transport compression. A producer MAY offer additional representations, but the advertised JSON representation remains normative.
|
|
97
|
+
|
|
98
|
+
## Caching
|
|
99
|
+
|
|
100
|
+
Stable current-origin URLs can change after a deployment. They MUST be revalidatable and MUST NOT be marked `immutable`. A suitable default is:
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
Cache-Control: public, max-age=0, must-revalidate
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Content-addressed URLs and candidate origins whose hostname is derived from the resulting Sources digest MAY use:
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
Cache-Control: public, max-age=31536000, immutable
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Changes status responses change over time and MUST use `Cache-Control: no-store`.
|
|
113
|
+
|
|
114
|
+
## Capability documents
|
|
115
|
+
|
|
116
|
+
- Read [openship-sources.md](openship-sources.md) for source snapshots and integrity.
|
|
117
|
+
- Read [openship-changes.md](openship-changes.md) for candidate changes and isolation.
|
|
118
|
+
- Read [openship-systems.md](openship-systems.md) for the self-contained systems model.
|
|
119
|
+
|
|
120
|
+
## Conformance
|
|
121
|
+
|
|
122
|
+
A producer is conformant for a capability when its advertised documents pass the relevant schema and every cross-document invariant in that capability specification. Advertising one capability does not claim conformance for another.
|
|
123
|
+
|
|
124
|
+
A consumer SHOULD report a precise path and invariant when rejecting a payload. It MUST reject unsupported major versions rather than silently interpreting them as v1.
|
|
125
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openship.dev/schemas/changes-accepted.schema.json",
|
|
4
|
+
"title": "OpenShip Changes v1 accepted response",
|
|
5
|
+
"$ref": "changes-status.schema.json",
|
|
6
|
+
"allOf": [
|
|
7
|
+
{
|
|
8
|
+
"type": "object",
|
|
9
|
+
"required": ["statusUrl"],
|
|
10
|
+
"properties": {
|
|
11
|
+
"status": { "enum": ["pending", "processing", "ready"] },
|
|
12
|
+
"statusUrl": { "type": "string", "format": "uri" }
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openship.dev/schemas/changes-policy.schema.json",
|
|
4
|
+
"title": "OpenShip Changes v1 policy",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["openship", "capability", "writable", "protected", "limits"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"openship": { "const": "1.0" },
|
|
9
|
+
"capability": { "const": "changes" },
|
|
10
|
+
"writable": { "type": "array", "items": { "$ref": "#/$defs/pattern" }, "uniqueItems": true },
|
|
11
|
+
"protected": { "type": "array", "items": { "$ref": "#/$defs/pattern" }, "uniqueItems": true },
|
|
12
|
+
"limits": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"required": ["filesPerChange", "bytesPerFile", "bytesPerChange"],
|
|
15
|
+
"properties": {
|
|
16
|
+
"filesPerChange": { "type": "integer", "minimum": 1 },
|
|
17
|
+
"bytesPerFile": { "type": "integer", "minimum": 1 },
|
|
18
|
+
"bytesPerChange": { "type": "integer", "minimum": 1 }
|
|
19
|
+
},
|
|
20
|
+
"additionalProperties": true
|
|
21
|
+
},
|
|
22
|
+
"contentRules": {
|
|
23
|
+
"type": "array",
|
|
24
|
+
"items": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": ["id", "rule", "message"],
|
|
27
|
+
"properties": {
|
|
28
|
+
"id": { "type": "string", "minLength": 1 },
|
|
29
|
+
"rule": { "type": "string", "minLength": 1 },
|
|
30
|
+
"message": { "type": "string", "minLength": 1 }
|
|
31
|
+
},
|
|
32
|
+
"additionalProperties": true
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"document": { "type": "string", "format": "uri" },
|
|
36
|
+
"payment": { "type": "object", "additionalProperties": true },
|
|
37
|
+
"authorization": { "type": "object", "additionalProperties": true }
|
|
38
|
+
},
|
|
39
|
+
"$defs": {
|
|
40
|
+
"pattern": { "type": "string", "minLength": 1, "pattern": "^(?!/)(?:[^*]+|[^*]+/\\*\\*)$" }
|
|
41
|
+
},
|
|
42
|
+
"additionalProperties": true
|
|
43
|
+
}
|
|
44
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openship.dev/schemas/changes-status.schema.json",
|
|
4
|
+
"title": "OpenShip Changes v1 status",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["openship", "capability", "changeId", "base", "digest", "status", "candidateOrigin"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"openship": { "const": "1.0" },
|
|
9
|
+
"capability": { "const": "changes" },
|
|
10
|
+
"changeId": { "type": "string", "minLength": 1 },
|
|
11
|
+
"base": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
|
|
12
|
+
"digest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
|
|
13
|
+
"status": { "enum": ["pending", "processing", "ready", "rejected", "failed"] },
|
|
14
|
+
"phase": { "type": "string", "minLength": 1 },
|
|
15
|
+
"candidateOrigin": { "type": "string", "format": "uri" },
|
|
16
|
+
"statusUrl": { "type": "string", "format": "uri" },
|
|
17
|
+
"buildId": { "type": "string", "minLength": 1 },
|
|
18
|
+
"reason": { "type": ["string", "null"] }
|
|
19
|
+
},
|
|
20
|
+
"additionalProperties": true
|
|
21
|
+
}
|
|
22
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openship.dev/schemas/changes-submission.schema.json",
|
|
4
|
+
"title": "OpenShip Changes v1 submission",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["openship", "capability", "base", "title", "intent", "files"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"openship": { "const": "1.0" },
|
|
9
|
+
"capability": { "const": "changes" },
|
|
10
|
+
"base": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
|
|
11
|
+
"title": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
12
|
+
"intent": { "type": "string", "minLength": 1, "maxLength": 4000 },
|
|
13
|
+
"files": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"minProperties": 1,
|
|
16
|
+
"propertyNames": { "type": "string", "minLength": 1, "maxLength": 512, "pattern": "^(?!/)(?!.*\\\\)(?!.*(?:^|/)\\.{1,2}(?:/|$))(?!.*//)[^\\u0000]+$" },
|
|
17
|
+
"additionalProperties": {
|
|
18
|
+
"oneOf": [
|
|
19
|
+
{ "type": "null" },
|
|
20
|
+
{
|
|
21
|
+
"type": "object",
|
|
22
|
+
"required": ["encoding", "content"],
|
|
23
|
+
"properties": {
|
|
24
|
+
"encoding": { "enum": ["utf-8", "base64"] },
|
|
25
|
+
"content": { "type": "string" }
|
|
26
|
+
},
|
|
27
|
+
"additionalProperties": false
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"additionalProperties": true
|
|
34
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openship.dev/schemas/changes-violation.schema.json",
|
|
4
|
+
"title": "OpenShip Changes v1 synchronous violation response",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["openship", "capability", "error", "message", "violations"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"openship": { "const": "1.0" },
|
|
9
|
+
"capability": { "const": "changes" },
|
|
10
|
+
"error": { "enum": ["stale_base", "policy_violation"] },
|
|
11
|
+
"message": { "type": "string", "minLength": 1 },
|
|
12
|
+
"base": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
|
|
13
|
+
"policy": { "type": "string", "format": "uri" },
|
|
14
|
+
"violations": {
|
|
15
|
+
"type": "array",
|
|
16
|
+
"minItems": 1,
|
|
17
|
+
"items": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"required": ["gate", "rule", "message"],
|
|
20
|
+
"properties": {
|
|
21
|
+
"gate": { "enum": ["envelope", "path", "size", "content", "tree"] },
|
|
22
|
+
"rule": { "type": "string", "minLength": 1 },
|
|
23
|
+
"path": { "type": "string", "minLength": 1 },
|
|
24
|
+
"line": { "type": "integer", "minimum": 1 },
|
|
25
|
+
"message": { "type": "string", "minLength": 1 }
|
|
26
|
+
},
|
|
27
|
+
"additionalProperties": true
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"additionalProperties": true
|
|
32
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openship.dev/schemas/discovery.schema.json",
|
|
4
|
+
"title": "OpenShip v1 discovery",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["openship", "capability", "project", "capabilities"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"openship": { "const": "1.0" },
|
|
9
|
+
"capability": { "const": "discovery" },
|
|
10
|
+
"project": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"required": ["name", "description"],
|
|
13
|
+
"properties": {
|
|
14
|
+
"name": { "type": "string", "minLength": 1 },
|
|
15
|
+
"description": { "type": "string", "minLength": 1 }
|
|
16
|
+
},
|
|
17
|
+
"additionalProperties": true
|
|
18
|
+
},
|
|
19
|
+
"skill": { "$ref": "#/$defs/httpsUrl" },
|
|
20
|
+
"capabilities": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"required": ["sources"],
|
|
23
|
+
"properties": {
|
|
24
|
+
"sources": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": ["manifest", "bundle"],
|
|
27
|
+
"properties": {
|
|
28
|
+
"manifest": { "$ref": "#/$defs/httpsUrl" },
|
|
29
|
+
"bundle": { "$ref": "#/$defs/httpsUrl" },
|
|
30
|
+
"file": { "type": "string", "pattern": "^https://.+\\{path\\}.+$|^https://.+/\\{path\\}$" },
|
|
31
|
+
"archive": { "$ref": "#/$defs/httpsUrl" },
|
|
32
|
+
"instructions": { "$ref": "#/$defs/httpsUrl" }
|
|
33
|
+
},
|
|
34
|
+
"additionalProperties": true
|
|
35
|
+
},
|
|
36
|
+
"changes": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"required": ["policy", "submit", "status"],
|
|
39
|
+
"properties": {
|
|
40
|
+
"policy": { "$ref": "#/$defs/httpsUrl" },
|
|
41
|
+
"submit": { "$ref": "#/$defs/httpsUrl" },
|
|
42
|
+
"status": { "type": "string", "pattern": "^https://.+\\{changeId\\}.+$|^https://.+/\\{changeId\\}$" }
|
|
43
|
+
},
|
|
44
|
+
"additionalProperties": true
|
|
45
|
+
},
|
|
46
|
+
"systems": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"required": ["document"],
|
|
49
|
+
"properties": { "document": { "$ref": "#/$defs/httpsUrl" } },
|
|
50
|
+
"additionalProperties": true
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"additionalProperties": true
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"$defs": {
|
|
57
|
+
"httpsUrl": { "type": "string", "format": "uri", "pattern": "^https://" }
|
|
58
|
+
},
|
|
59
|
+
"additionalProperties": true
|
|
60
|
+
}
|
|
61
|
+
|