@mnstry/atelier 0.2.0-alpha.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/CHANGELOG.md +383 -0
- package/LICENSE +202 -0
- package/NOTICE +5 -0
- package/README.md +262 -0
- package/SECURITY.md +93 -0
- package/TRADEMARKS.md +88 -0
- package/announcements/2026-08-06-announcements-channel.v1.json +12 -0
- package/announcements/keys/mnstry-announcements.public.v1.json +9 -0
- package/bin/atelier.mjs +2 -0
- package/bin/mnstry-atelier.mjs +3 -0
- package/contracts/analysis-adapter.v1.schema.json +248 -0
- package/contracts/atelier-action-intent.v1.schema.json +143 -0
- package/contracts/atelier-analysis-adapter.v1.schema.json +360 -0
- package/contracts/atelier-attestation.v1.schema.json +239 -0
- package/contracts/atelier-boundary-policy.v1.schema.json +339 -0
- package/contracts/atelier-claim.v1.schema.json +97 -0
- package/contracts/atelier-export.v1.schema.json +1015 -0
- package/contracts/atelier-extension-pack.v1.schema.json +290 -0
- package/contracts/atelier-kit-manifest.v1.schema.json +273 -0
- package/contracts/atelier-lock.v1.schema.json +364 -0
- package/contracts/atelier-migration.v1.schema.json +162 -0
- package/contracts/atelier-project-config.v1.schema.json +352 -0
- package/contracts/atelier-readiness-protocol.v1.schema.json +517 -0
- package/contracts/atelier-readiness-run.v1.schema.json +229 -0
- package/contracts/atelier-readiness.v1.schema.json +457 -0
- package/contracts/compat-baseline.json +10 -0
- package/contracts/git-promote-event.v1.schema.json +145 -0
- package/contracts/knowledge-source-sidecar.v1.schema.json +157 -0
- package/contracts/mnstry-atelier-semantic-profile.v1.json +254 -0
- package/docs/announcements.md +101 -0
- package/docs/atelier-runtime.md +34 -0
- package/docs/attestation.md +201 -0
- package/docs/continuity.md +55 -0
- package/docs/contract-stability.md +111 -0
- package/docs/distributions.md +220 -0
- package/docs/install.md +140 -0
- package/docs/knowledge-graph.md +140 -0
- package/docs/ontology.md +206 -0
- package/docs/release-engineering.md +151 -0
- package/docs/repo-boundary-guard.md +199 -0
- package/docs/tenant-readiness.md +64 -0
- package/docs/upgrade.md +124 -0
- package/fixtures/analysis/invalid/native-graph.v1.json +4 -0
- package/fixtures/analysis/invalid/promoted-claim.v1.json +17 -0
- package/fixtures/analysis/invalid/runtime-import.v1.json +20 -0
- package/fixtures/analysis/valid/output.claims.v1.json +17 -0
- package/fixtures/analysis-adapter/invalid/manifest-hidden-provider.v1.json +24 -0
- package/fixtures/analysis-adapter/invalid/output-frontmatter-mutation.v1.json +21 -0
- package/fixtures/analysis-adapter/invalid/output-native-graph.v1.json +14 -0
- package/fixtures/analysis-adapter/manifest.disabled.v1.json +22 -0
- package/fixtures/analysis-adapter/manifest.enabled-local.v1.json +23 -0
- package/fixtures/analysis-adapter/output.claims.v1.json +21 -0
- package/fixtures/atelier-action-intent/invalid/runtime-mutation-authority.v1.json +16 -0
- package/fixtures/atelier-action-intent/invalid/unknown-top-level-field.v1.json +17 -0
- package/fixtures/atelier-action-intent/invalid/writes-beyond-clipboard.v1.json +16 -0
- package/fixtures/atelier-action-intent/valid/copy-agent-prompt.v1.json +16 -0
- package/fixtures/atelier-action-intent/valid/copy-repo-path.v1.json +22 -0
- package/fixtures/atelier-analysis-adapter/invalid/hidden-model-provider.v1.json +24 -0
- package/fixtures/atelier-analysis-adapter/invalid/inert-with-execute-command.v1.json +24 -0
- package/fixtures/atelier-analysis-adapter/invalid/proposals-with-runtime-import.v1.json +30 -0
- package/fixtures/atelier-analysis-adapter/valid/manifest-inert.v1.json +24 -0
- package/fixtures/atelier-analysis-adapter/valid/proposals-claim-only.v1.json +27 -0
- package/fixtures/atelier-attestation/invalid/bad-digest.v1.json +23 -0
- package/fixtures/atelier-attestation/invalid/conformance-scope.v1.json +23 -0
- package/fixtures/atelier-attestation/invalid/missing-signature.v1.json +22 -0
- package/fixtures/atelier-attestation/invalid/unknown-root-field.v1.json +24 -0
- package/fixtures/atelier-attestation/keys/roundtrip-issuer.public.v1.json +9 -0
- package/fixtures/atelier-attestation/valid/rejected-with-reasons.v1.json +49 -0
- package/fixtures/atelier-attestation/valid/signed-admitted.v1.json +35 -0
- package/fixtures/atelier-attestation/valid/signed-roundtrip.v1.json +29 -0
- package/fixtures/atelier-attestation/valid/unsigned-advisory.v1.json +23 -0
- package/fixtures/atelier-claim/invalid/unknown-provider.v1.json +15 -0
- package/fixtures/atelier-claim/invalid/unknown-top-level-field.v1.json +16 -0
- package/fixtures/atelier-claim/invalid/wrong-schema-const.v1.json +15 -0
- package/fixtures/atelier-claim/valid/adapter-style-proposed-relation.v1.json +19 -0
- package/fixtures/atelier-claim/valid/codex-proposed-relation.v1.json +16 -0
- package/fixtures/atelier-claim/valid/manual-proposed-relation.v1.json +16 -0
- package/fixtures/atelier-claim/valid/readiness-proposed-relation.v1.json +18 -0
- package/fixtures/atelier-export/invalid/dirty-unforced-empty-taint.v1.json +19 -0
- package/fixtures/atelier-export/invalid/object-local-audience-visibility.v1.json +11 -0
- package/fixtures/atelier-export/invalid/open-object-class-owner.v1.json +23 -0
- package/fixtures/atelier-export/invalid/pricing-legacy-amount.v1.json +23 -0
- package/fixtures/atelier-export/invalid/public-disguised-internal-source-ref.v1.json +11 -0
- package/fixtures/atelier-export/invalid/public-nested-private-source-ref.v1.json +17 -0
- package/fixtures/atelier-export/invalid/public-resolved-private-source-ref.v1.json +13 -0
- package/fixtures/atelier-export/invalid/public-resolved-sensitive-source-ref.v1.json +13 -0
- package/fixtures/atelier-export/invalid/public-source-ref-missing-provenance.v1.json +20 -0
- package/fixtures/atelier-export/invalid/public-source-team-kg.json +12 -0
- package/fixtures/atelier-export/invalid/source-node-legacy-visibility.v1.json +21 -0
- package/fixtures/atelier-export/invalid/source-node-unknown-field.v1.json +18 -0
- package/fixtures/atelier-export/invalid/unresolved-public-source-ref.v1.json +11 -0
- package/fixtures/atelier-export/sample-studio-offer.v1.json +397 -0
- package/fixtures/atelier-extension-pack/invalid/bundled-slug-collision.v1.json +22 -0
- package/fixtures/atelier-extension-pack/invalid/escapes-root.v1.json +22 -0
- package/fixtures/atelier-extension-pack/invalid/posture-tampered.v1.json +22 -0
- package/fixtures/atelier-extension-pack/invalid/protocols/autonomous-authority.v1.json +118 -0
- package/fixtures/atelier-extension-pack/invalid/protocols/offer-map.v1.json +109 -0
- package/fixtures/atelier-extension-pack/invalid/redefines-runtime-term.v1.json +19 -0
- package/fixtures/atelier-extension-pack/invalid/wrong-namespace.v1.json +19 -0
- package/fixtures/atelier-extension-pack/valid/protocols/contract-gate.v1.json +109 -0
- package/fixtures/atelier-extension-pack/valid/sample-pack.v1.json +48 -0
- package/fixtures/atelier-kit-manifest/invalid/runtime-copy-without-canonical-role.v1.json +53 -0
- package/fixtures/atelier-kit-manifest/invalid/telemetry-send-path.v1.json +21 -0
- package/fixtures/atelier-kit-manifest/valid/mnstry-atelier.valid.v1.json +76 -0
- package/fixtures/atelier-lock/invalid/absolute-contract-path.v1.json +40 -0
- package/fixtures/atelier-lock/invalid/runtime-mutation-enabled.v1.json +41 -0
- package/fixtures/atelier-lock/invalid/unknown-top-level-field.v1.json +42 -0
- package/fixtures/atelier-lock/valid/extension-pack.v1.json +55 -0
- package/fixtures/atelier-lock/valid/minimal.v1.json +44 -0
- package/fixtures/atelier-migration/invalid/live-apply-mode.v1.json +21 -0
- package/fixtures/atelier-migration/invalid/missing-review-evidence.v1.json +20 -0
- package/fixtures/atelier-migration/invalid/runtime-mutation-enabled.v1.json +21 -0
- package/fixtures/atelier-migration/valid/minimal.v1.json +32 -0
- package/fixtures/atelier-project-config/invalid/absolute-repo-path.v1.json +11 -0
- package/fixtures/atelier-project-config/invalid/ext-not-object.v1.json +12 -0
- package/fixtures/atelier-project-config/invalid/external-kind-read-boundary.v1.json +12 -0
- package/fixtures/atelier-project-config/invalid/missing-repos-and-alignment.v1.json +7 -0
- package/fixtures/atelier-project-config/invalid/unknown-top-level-field.v1.json +14 -0
- package/fixtures/atelier-project-config/valid/alignment-scaffold.v1.json +15 -0
- package/fixtures/atelier-project-config/valid/ext-extension-packs.v1.json +36 -0
- package/fixtures/atelier-project-config/valid/repo-workspace.v1.json +23 -0
- package/fixtures/atelier-project-config/validator-only/ext-member-not-object.v1.json +14 -0
- package/fixtures/atelier-readiness/invalid/dry-run-only-false.v1.json +87 -0
- package/fixtures/atelier-readiness/invalid/support-bundle-send-path.v1.json +87 -0
- package/fixtures/atelier-readiness/invalid/unknown-top-level-field.v1.json +88 -0
- package/fixtures/atelier-readiness/valid/ready-workspace.v1.json +84 -0
- package/fixtures/boundary-policy/invalid/content-rule-exception-blanket-path.v1.json +67 -0
- package/fixtures/boundary-policy/invalid/invalid-actor-private-repo-mapping.v1.json +20 -0
- package/fixtures/boundary-policy/invalid/legacy-warning-missing-warning.v1.json +20 -0
- package/fixtures/boundary-policy/invalid/missing-repo-coverage.v1.json +19 -0
- package/fixtures/boundary-policy/invalid/unknown-key.v1.json +12 -0
- package/fixtures/boundary-policy/valid/content-rule-exception.v1.json +67 -0
- package/fixtures/boundary-policy/valid/legacy-warning.v1.json +33 -0
- package/fixtures/boundary-policy/valid/strict-private-shared.v1.json +40 -0
- package/fixtures/git-promote/invalid/provision-named-revocable.v1.json +29 -0
- package/fixtures/git-promote/private-to-team.v1.json +29 -0
- package/fixtures/knowledge-source-sidecar/invalid/legacy-visibility-field.v1.json +17 -0
- package/fixtures/knowledge-source-sidecar/invalid/missing-audience.v1.json +15 -0
- package/fixtures/knowledge-source-sidecar/invalid/unknown-relation-kind.v1.json +18 -0
- package/fixtures/knowledge-source-sidecar/valid/html-source.v1.json +18 -0
- package/fixtures/knowledge-source-sidecar/valid/pdf-evidence.v1.json +18 -0
- package/fixtures/projects/neutral-project/atelier.project.json +15 -0
- package/fixtures/projects/neutral-project/ops/config/repo-access.v1.json +12 -0
- package/fixtures/projects/neutral-project/project-knowledge.graph.json +69 -0
- package/fixtures/projects/sample-workspace/atelier.project.json +23 -0
- package/fixtures/projects/sample-workspace/boundary-policy.v1.json +59 -0
- package/fixtures/projects/sample-workspace/content/README.md +16 -0
- package/fixtures/projects/sample-workspace/content/source.html +5 -0
- package/fixtures/projects/sample-workspace/content/source.html.kg.json +15 -0
- package/fixtures/projects/sample-workspace/repo-access.v1.json +9 -0
- package/fixtures/projects/source-formats-workspace/atelier.project.json +23 -0
- package/fixtures/projects/source-formats-workspace/content/README.md +17 -0
- package/fixtures/projects/source-formats-workspace/content/data.json +7 -0
- package/fixtures/projects/source-formats-workspace/content/data.json.kg.json +15 -0
- package/fixtures/projects/source-formats-workspace/content/logo.png.kg.json +15 -0
- package/fixtures/projects/source-formats-workspace/content/metrics.csv +3 -0
- package/fixtures/projects/source-formats-workspace/content/metrics.csv.kg.json +15 -0
- package/fixtures/projects/source-formats-workspace/content/pipeline.yaml +4 -0
- package/fixtures/projects/source-formats-workspace/content/pipeline.yaml.kg.json +15 -0
- package/fixtures/projects/source-formats-workspace/repo-access.v1.json +9 -0
- package/fixtures/readiness-protocols/extension-pack/invalid/protocol-missing-path.v1.json +20 -0
- package/fixtures/readiness-protocols/extension-pack/valid/with-protocol.v1.json +24 -0
- package/fixtures/readiness-protocols/protocol/invalid/missing-check-target.v1.json +103 -0
- package/fixtures/readiness-protocols/protocol/invalid/runtime-mutation-enabled.v1.json +107 -0
- package/fixtures/readiness-protocols/protocol/valid/minimal.v1.json +100 -0
- package/fixtures/readiness-protocols/run/invalid/missing-evidence.v1.json +37 -0
- package/fixtures/readiness-protocols/run/invalid/pass-with-blocker.v1.json +22 -0
- package/fixtures/readiness-protocols/run/invalid/runtime-mutation-enabled.v1.json +22 -0
- package/fixtures/readiness-protocols/run/valid/pass.v1.json +44 -0
- package/fixtures/support/invalid/leaky-support.v1.json +13 -0
- package/fixtures/support/valid/minimal-safe.v1.json +20 -0
- package/package.json +98 -0
- package/skills/claude/atelier-open/SKILL.md +68 -0
- package/skills/claude/mnstry-readiness/SKILL.md +41 -0
- package/skills/codex/atelier-open/SKILL.md +68 -0
- package/skills/codex/mnstry-readiness/SKILL.md +41 -0
- package/src/analysis/adapter.mjs +20 -0
- package/src/analysis/analysis-claim-contract.mjs +335 -0
- package/src/attestation/jcs.mjs +64 -0
- package/src/attestation/sign.mjs +305 -0
- package/src/boundary/content-rules.mjs +262 -0
- package/src/boundary/policy.mjs +806 -0
- package/src/check-atelier-export-contract.mjs +116 -0
- package/src/cli/run.mjs +283 -0
- package/src/collaboration/proposals.mjs +262 -0
- package/src/commands/analysis.mjs +8 -0
- package/src/commands/announcements.mjs +308 -0
- package/src/commands/attestation.mjs +198 -0
- package/src/commands/boundary.mjs +25 -0
- package/src/commands/config.mjs +4 -0
- package/src/commands/context.mjs +3 -0
- package/src/commands/distribution.mjs +224 -0
- package/src/commands/egress.mjs +11 -0
- package/src/commands/extension-pack.mjs +209 -0
- package/src/commands/feedback.mjs +3 -0
- package/src/commands/graph.mjs +3 -0
- package/src/commands/init.mjs +130 -0
- package/src/commands/lock.mjs +3 -0
- package/src/commands/project.mjs +3 -0
- package/src/commands/promote.mjs +4 -0
- package/src/commands/readiness.mjs +3 -0
- package/src/commands/server.mjs +6 -0
- package/src/commands/setup.mjs +250 -0
- package/src/commands/support.mjs +3 -0
- package/src/commands/upgrade.mjs +3 -0
- package/src/contracts/corpus.mjs +171 -0
- package/src/egress/check.mjs +45 -0
- package/src/egress/forbidden-egress.mjs +323 -0
- package/src/export/atelier-export-contract.mjs +722 -0
- package/src/extension-packs/loader.mjs +491 -0
- package/src/graph/graph.mjs +370 -0
- package/src/graph/knowledge-graph.mjs +887 -0
- package/src/harness/context-client.mjs +144 -0
- package/src/harness/context.mjs +27 -0
- package/src/index.mjs +88 -0
- package/src/project/config.mjs +557 -0
- package/src/project/file-class.mjs +113 -0
- package/src/project/git-ignore.mjs +31 -0
- package/src/project/path-match.mjs +30 -0
- package/src/project/repo-identity.mjs +176 -0
- package/src/projection/alignment-projection.mjs +169 -0
- package/src/projection/policy.mjs +90 -0
- package/src/projection/project.mjs +152 -0
- package/src/readiness/readiness.mjs +312 -0
- package/src/readiness-protocols/bundled-pack.mjs +766 -0
- package/src/readiness-protocols/runtime.mjs +419 -0
- package/src/server/local-sidecar.mjs +640 -0
- package/src/server/security.mjs +229 -0
- package/src/server/server.mjs +42 -0
- package/src/support/feedback-report.mjs +414 -0
- package/src/support/support-bundle.mjs +226 -0
- package/src/ui/html-primitives.mjs +104 -0
- package/src/upgrade/upgrade.mjs +701 -0
- package/src/validate-atelier-export-dry-run.mjs +75 -0
- package/templates/atelier.project.example.json +23 -0
- package/templates/distribution-workspace/.claude/launch.json +12 -0
- package/templates/distribution-workspace/README.md +86 -0
- package/templates/distribution-workspace/atelier.project.json +36 -0
- package/templates/distribution-workspace/boundary-policy.v1.json +61 -0
- package/templates/distribution-workspace/gitignore +29 -0
- package/templates/distribution-workspace/governance/repo-boundary-ledger.md +5 -0
- package/templates/distribution-workspace/project/README.md +19 -0
- package/templates/distribution-workspace/repo-access.v1.json +9 -0
- package/templates/launch.json +12 -0
- package/templates/private-domain-workspace/.claude/launch.json +12 -0
- package/templates/private-domain-workspace/README.md +50 -0
- package/templates/private-domain-workspace/atelier.project.json +28 -0
- package/templates/private-domain-workspace/boundary-policy.v1.json +54 -0
- package/templates/private-domain-workspace/domain/README.md +19 -0
- package/templates/private-domain-workspace/gitignore +29 -0
- package/templates/private-domain-workspace/governance/repo-boundary-ledger.md +4 -0
- package/templates/private-domain-workspace/repo-access.v1.json +9 -0
- package/templates/shared-project-workspace/.claude/launch.json +12 -0
- package/templates/shared-project-workspace/README.md +51 -0
- package/templates/shared-project-workspace/atelier.project.json +28 -0
- package/templates/shared-project-workspace/boundary-policy.v1.json +61 -0
- package/templates/shared-project-workspace/gitignore +29 -0
- package/templates/shared-project-workspace/governance/repo-boundary-ledger.md +5 -0
- package/templates/shared-project-workspace/project/README.md +19 -0
- package/templates/shared-project-workspace/repo-access.v1.json +9 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# Attestation
|
|
2
|
+
|
|
3
|
+
An attestation is a small governed document (`atelier-attestation@v1`,
|
|
4
|
+
`contracts/atelier-attestation.v1.schema.json`) in which an issuer records an
|
|
5
|
+
admission decision about one payload document — an export, a readiness run, or
|
|
6
|
+
a claim. It binds that decision to the exact payload bytes through a canonical
|
|
7
|
+
hash, names the issuer, and either carries a signature or explicitly declares
|
|
8
|
+
itself unsigned.
|
|
9
|
+
|
|
10
|
+
An attestation is a record of a decision, not the decision mechanism. It never
|
|
11
|
+
grants runtime authority, never mutates anything, and never substitutes for
|
|
12
|
+
running the published validators yourself.
|
|
13
|
+
|
|
14
|
+
## Admission versus conformance
|
|
15
|
+
|
|
16
|
+
The two words are deliberately kept apart, and the schema enforces the split
|
|
17
|
+
structurally:
|
|
18
|
+
|
|
19
|
+
- Conformance is public and offline. Anyone can run the published validators
|
|
20
|
+
in this package against a document and reach the same answer. No attestation
|
|
21
|
+
is needed to establish conformance, and no attestation can establish it.
|
|
22
|
+
- Admission is a decision by a specific issuer to accept a specific payload
|
|
23
|
+
into a specific destination. An attestation only records an admission
|
|
24
|
+
decision. MNSTRY's private validator only ever decides MNSTRY admission; it
|
|
25
|
+
does not define, extend, or gate conformance.
|
|
26
|
+
|
|
27
|
+
`verdict.scope` is the constant `"admission"`. A document claiming a
|
|
28
|
+
conformance verdict cannot validate against the schema, so the vocabulary
|
|
29
|
+
cannot drift into "certified conformant" territory by accident.
|
|
30
|
+
|
|
31
|
+
The admission decision itself is one of `admitted`, `rejected`, or
|
|
32
|
+
`needs-review`, with optional machine-readable `reasons` entries
|
|
33
|
+
(`code` + `message`) explaining a rejection or review request.
|
|
34
|
+
|
|
35
|
+
## Canonical payload hash
|
|
36
|
+
|
|
37
|
+
`subject.payloadHash` binds the attestation to exact payload content. The
|
|
38
|
+
procedure is normative:
|
|
39
|
+
|
|
40
|
+
1. Canonicalize the attested payload document with RFC 8785 (JSON
|
|
41
|
+
Canonicalization Scheme, JCS).
|
|
42
|
+
2. Hash the canonical bytes with SHA-256.
|
|
43
|
+
3. Encode the digest as lowercase hexadecimal (64 characters).
|
|
44
|
+
|
|
45
|
+
The fields are pinned accordingly: `algorithm` is the constant `"sha-256"`,
|
|
46
|
+
`canonicalization` is the constant `"RFC8785-JCS"`, and `digest` must match
|
|
47
|
+
`^[0-9a-f]{64}$`. A relying party that re-canonicalizes the payload and gets a
|
|
48
|
+
different digest must treat the attestation as not applying to that payload.
|
|
49
|
+
|
|
50
|
+
This package ships the reference implementation of this procedure —
|
|
51
|
+
`src/attestation/jcs.mjs` (RFC 8785 canonicalization), `src/attestation/sign.mjs`
|
|
52
|
+
(hashing, signing, verification), and the `atelier attestation hash|sign|verify`
|
|
53
|
+
command line. The contract fixes the procedure and the encoding; the
|
|
54
|
+
implementation is replaceable by anything that produces the same bytes.
|
|
55
|
+
|
|
56
|
+
`subject.schema` names the contract of the attested payload (for example
|
|
57
|
+
`atelier-export@v1`), `subject.ref` carries its stable identifier (exportId,
|
|
58
|
+
runId, or claimId), and optional `subject.references` entries cross-reference
|
|
59
|
+
related claims, runs, exports, or protocols.
|
|
60
|
+
|
|
61
|
+
## Signature semantics
|
|
62
|
+
|
|
63
|
+
`signature` is required in shape and nullable in value. Every attestation must
|
|
64
|
+
say one of two things — silently absent is not expressible:
|
|
65
|
+
|
|
66
|
+
- `"signature": null` — the attestation is unsigned and therefore
|
|
67
|
+
non-authoritative. It is advisory only: useful as a local record or a
|
|
68
|
+
tooling artifact, but it proves nothing about who issued it.
|
|
69
|
+
- A signature object — `algorithm` (`ed25519` or `es256`), `keyId`, and a
|
|
70
|
+
base64url `value`. An admission decision is authoritative only when it
|
|
71
|
+
carries a signature whose `keyId` the relying party recognizes as belonging
|
|
72
|
+
to the claimed issuer.
|
|
73
|
+
|
|
74
|
+
The `issuer` block asserts identity (`id`, `role`, optional `keyId`); the
|
|
75
|
+
signature is what makes that assertion checkable. `issuer.role` distinguishes
|
|
76
|
+
an admission authority from a tool or a self-attestation, but the role field
|
|
77
|
+
alone confers no authority.
|
|
78
|
+
|
|
79
|
+
Verifier implementation is in scope (see the verification procedure below).
|
|
80
|
+
Key distribution and trust establishment remain policy-level: relying parties
|
|
81
|
+
decide which keys they recognize, and MNSTRY publishes its admission public
|
|
82
|
+
keys out of band.
|
|
83
|
+
|
|
84
|
+
## Signing procedure
|
|
85
|
+
|
|
86
|
+
The signing input is the UTF-8 encoding of the RFC 8785 (JCS) canonicalization
|
|
87
|
+
of the attestation document with its `signature` member set to `null`. That
|
|
88
|
+
sentence is normative; everything else here is mechanics.
|
|
89
|
+
|
|
90
|
+
- To sign: take a schema-valid attestation whose `signature` is `null`,
|
|
91
|
+
canonicalize it, sign the canonical bytes, and embed the result as
|
|
92
|
+
`{ "algorithm": ..., "keyId": ..., "value": ... }` where `value` is the
|
|
93
|
+
unpadded base64url encoding of the raw signature bytes.
|
|
94
|
+
- To verify: take the signed document, replace `signature` with `null`,
|
|
95
|
+
canonicalize, and verify `signature.value` over those bytes.
|
|
96
|
+
- `ed25519` signatures are raw 64-byte RFC 8032 signatures.
|
|
97
|
+
- `es256` signatures are the 64-byte IEEE P1363 concatenation of r and s,
|
|
98
|
+
not DER. A DER-encoded signature also fits the schema's base64url pattern,
|
|
99
|
+
so a verifier must not accept one by accident and a signer must never emit
|
|
100
|
+
one — both signature shapes are 64 bytes (86 base64url characters).
|
|
101
|
+
|
|
102
|
+
A signer must refuse to sign a document that is not schema-valid, already
|
|
103
|
+
carries a signature, or names an `issuer.keyId` different from the signing
|
|
104
|
+
key's `keyId`.
|
|
105
|
+
|
|
106
|
+
## Verification procedure
|
|
107
|
+
|
|
108
|
+
`verifyAttestation` returns `{ valid, reasons }` and collects every applicable
|
|
109
|
+
reason instead of stopping at the first, in this order:
|
|
110
|
+
|
|
111
|
+
1. `schema.invalid` — the document fails the attestation schema.
|
|
112
|
+
2. `signature.missing` — `signature` is `null`. An unsigned attestation is
|
|
113
|
+
non-authoritative, so verification fails by definition.
|
|
114
|
+
3. `signature.algorithm-mismatch` — `signature.algorithm` does not match the
|
|
115
|
+
public key document's `algorithm` (or its JWK is for another curve).
|
|
116
|
+
4. `signature.key-id-mismatch` — `signature.keyId` does not match the public
|
|
117
|
+
key document's `keyId`.
|
|
118
|
+
5. `issuer.key-id-mismatch` — `issuer.keyId` is present and differs from
|
|
119
|
+
`signature.keyId`.
|
|
120
|
+
6. `signature.invalid` — cryptographic verification fails over the signing
|
|
121
|
+
input defined above.
|
|
122
|
+
|
|
123
|
+
Payload binding is checked separately by `verifyPayloadBinding`, with reasons
|
|
124
|
+
`payload.digest-mismatch` (recomputed canonical digest differs from
|
|
125
|
+
`subject.payloadHash.digest`) and `payload.schema-field-mismatch` (the
|
|
126
|
+
payload's `schema` field differs from `subject.schema`). Signature validity
|
|
127
|
+
and payload binding are independent answers: a signature can be genuine while
|
|
128
|
+
the attestation is being presented against the wrong payload, and vice versa.
|
|
129
|
+
|
|
130
|
+
## Key format and the local key file
|
|
131
|
+
|
|
132
|
+
Key material is handled as JWK only — never PEM. The private key file lives in
|
|
133
|
+
the signing project's working directory, is gitignored by default, and must
|
|
134
|
+
never be committed:
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"keyId": "example-issuer-2026",
|
|
139
|
+
"algorithm": "ed25519",
|
|
140
|
+
"privateKeyJwk": { "kty": "OKP", "crv": "Ed25519", "x": "wafd…", "d": "9dfu…" }
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The signing key loads with fail-closed precedence: the
|
|
145
|
+
`ATELIER_ATTESTATION_KEY_JSON` environment variable (key file JSON), then
|
|
146
|
+
`--key <file>`, then `./atelier-attestation-key.local.json` in the current
|
|
147
|
+
directory. If none is present, signing exits with code 2 — there is no
|
|
148
|
+
"sign without a key" mode.
|
|
149
|
+
|
|
150
|
+
The public key document is freely shareable and is what verifiers consume:
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"keyId": "example-issuer-2026",
|
|
155
|
+
"algorithm": "ed25519",
|
|
156
|
+
"publicKeyJwk": { "kty": "OKP", "crv": "Ed25519", "x": "wafd…" }
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Errors and logs may name `keyId` and `algorithm`, and never any JWK member
|
|
161
|
+
value.
|
|
162
|
+
|
|
163
|
+
## Command line
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
atelier attestation hash <payload.json>
|
|
167
|
+
atelier attestation sign <attestation.json> [--key FILE] [--out FILE]
|
|
168
|
+
atelier attestation verify <attestation.json> --public-key FILE [--payload FILE] [--json]
|
|
169
|
+
atelier attestation keygen --key-id ID [--algorithm ed25519|es256] [--out FILE]
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
- `hash` prints the canonical `payloadHash` object for a payload document.
|
|
173
|
+
- `sign` signs an unsigned attestation with the local signing key and writes
|
|
174
|
+
the signed document to stdout or `--out`.
|
|
175
|
+
- `verify` checks the signature against a public key file, plus payload
|
|
176
|
+
binding when `--payload` is given; `--json` prints `{ valid, reasons }`.
|
|
177
|
+
When no `--payload` is given, the output says so explicitly.
|
|
178
|
+
- `keygen` generates a key pair, writes the private key file (mode 0600,
|
|
179
|
+
refusing to overwrite an existing file), and prints only the public key
|
|
180
|
+
document.
|
|
181
|
+
|
|
182
|
+
Exit codes: 0 success (for `verify`: valid), 1 `verify` judged the attestation
|
|
183
|
+
invalid, 2 usage or input error.
|
|
184
|
+
|
|
185
|
+
## Fixtures and tests
|
|
186
|
+
|
|
187
|
+
Reference documents live in `fixtures/atelier-attestation/` (`valid/` and
|
|
188
|
+
`invalid/`), and `test/attestation-contract.test.mjs` validates all of them
|
|
189
|
+
against the schema, asserting that each invalid fixture fails for its intended
|
|
190
|
+
reason.
|
|
191
|
+
|
|
192
|
+
`valid/` fixtures are schema-valid shapes; only `signed-roundtrip.v1.json` is
|
|
193
|
+
also cryptographically real. Its signature was produced once at authoring time
|
|
194
|
+
with a throwaway key whose private half was never persisted; the public half
|
|
195
|
+
is committed as `keys/roundtrip-issuer.public.v1.json`, and its digest is the
|
|
196
|
+
real canonical hash of `fixtures/atelier-export/sample-studio-offer.v1.json`.
|
|
197
|
+
`test/attestation-signing.test.mjs` verifies the committed signature and the
|
|
198
|
+
payload binding on every run (and never re-signs), alongside a tamper matrix
|
|
199
|
+
asserting the exact failure reason for each mutation.
|
|
200
|
+
`test/attestation-jcs.test.mjs` pins the canonicalizer to the RFC 8785 vectors,
|
|
201
|
+
and `test/attestation-cli.test.mjs` exercises the command line end to end.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Distribution continuity commitment
|
|
2
|
+
|
|
3
|
+
Anyone building on this package needs to know their access cannot be revoked
|
|
4
|
+
by a repository permission change. This document is MNSTRY's answer, and it is
|
|
5
|
+
written to survive skeptical reading.
|
|
6
|
+
|
|
7
|
+
## What the license already guarantees
|
|
8
|
+
|
|
9
|
+
Every copy of this package you have received is licensed under Apache-2.0.
|
|
10
|
+
That grant is perpetual and irrevocable for the copy you hold: nothing MNSTRY
|
|
11
|
+
does later — repository changes, relicensing of future versions, commercial
|
|
12
|
+
disagreements — removes your right to use, modify, and redistribute the
|
|
13
|
+
version you received, subject only to the license's own terms (including the
|
|
14
|
+
NOTICE attribution obligation and the trademark limits in `TRADEMARKS.md`).
|
|
15
|
+
|
|
16
|
+
## What MNSTRY commits to beyond the license
|
|
17
|
+
|
|
18
|
+
1. **Source grant on every tag.** Every tagged release that MNSTRY
|
|
19
|
+
distributes to a collaborator or partner is theirs to keep. If your access
|
|
20
|
+
channel is the Git repository and that access ever ends, you retain every
|
|
21
|
+
tag you fetched, with full Apache-2.0 rights over it. MNSTRY will not ask
|
|
22
|
+
for deletion of received tagged source and has no license mechanism to do
|
|
23
|
+
so.
|
|
24
|
+
2. **npm publication at public release.** When the package's public flip
|
|
25
|
+
happens, tagged releases will be published to the npm registry under
|
|
26
|
+
`@mnstry/atelier` with public access, making the registry — not repository
|
|
27
|
+
permission — the distribution channel of record.
|
|
28
|
+
3. **Contract compatibility by machinery.** From tag `v0.2.0-alpha.0`
|
|
29
|
+
onward, the published contracts are under a compatibility gate
|
|
30
|
+
(`npm run contract:compat`): documents valid against the baseline tag must
|
|
31
|
+
stay valid, and schema widening outside `ext` containers is refused by a
|
|
32
|
+
schema-vs-schema differ. Breaking changes require a new major contract
|
|
33
|
+
version, recorded in `CHANGELOG.md` under a **Breaking** bullet and in the
|
|
34
|
+
migrations map.
|
|
35
|
+
4. **Conformance stays offline.** Validating documents against the published
|
|
36
|
+
contracts requires only this package and never a MNSTRY service. No future
|
|
37
|
+
version will make offline conformance depend on a network call — that is
|
|
38
|
+
enforced by the egress gate, not promised by intention.
|
|
39
|
+
|
|
40
|
+
## What this document does not promise
|
|
41
|
+
|
|
42
|
+
- It does not promise that unreleased work, private extension packs, or
|
|
43
|
+
MNSTRY's runtime services are or will become open.
|
|
44
|
+
- It does not promise support, maintenance windows, or acceptance of
|
|
45
|
+
contributions.
|
|
46
|
+
- It does not grant trademark rights; `TRADEMARKS.md` governs naming.
|
|
47
|
+
|
|
48
|
+
## Verifying instead of trusting
|
|
49
|
+
|
|
50
|
+
- The license grant: `LICENSE` (Apache-2.0, section 2 — "perpetual,
|
|
51
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable").
|
|
52
|
+
- The compatibility gate: `scripts/check-contract-compat.mjs` and
|
|
53
|
+
`contracts/compat-baseline.json`.
|
|
54
|
+
- The offline-conformance property: `npm run egress:check` and the served-CSP
|
|
55
|
+
assertions in the test suite.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Contract stability
|
|
2
|
+
|
|
3
|
+
How the Atelier contracts evolve without breaking pinned consumers: the
|
|
4
|
+
stability epoch, the `contractVersion` field, the reserved `ext` extension
|
|
5
|
+
containers and their must-ignore rules, the widening ban, the deprecation
|
|
6
|
+
policy, and the compatibility gate that enforces all of it mechanically.
|
|
7
|
+
|
|
8
|
+
## The stability epoch
|
|
9
|
+
|
|
10
|
+
The contracts underwent one sanctioned flag-day break — the epoch. All
|
|
11
|
+
breaking surgery landed together, before the next release tag: the
|
|
12
|
+
vendor-neutral runtime owner vocabulary, the `ext` containers, the optional
|
|
13
|
+
`contractVersion` field, the claim provider addition, and the schema const
|
|
14
|
+
alignments. The individual changes are listed in the changelog.
|
|
15
|
+
|
|
16
|
+
The first tag cut after this surgery is the epoch baseline. The compatibility
|
|
17
|
+
gate pins to it through `contracts/compat-baseline.json` and is deliberately
|
|
18
|
+
inert — a clean pass with a notice — until that tag exists.
|
|
19
|
+
|
|
20
|
+
From the epoch onward the rule is mechanical: any change that widens the
|
|
21
|
+
accepted document space outside `ext` is a new major version, and the gate
|
|
22
|
+
enforces this automatically, because validators pinned to the baseline reject
|
|
23
|
+
widened documents.
|
|
24
|
+
|
|
25
|
+
## Contract version
|
|
26
|
+
|
|
27
|
+
Every document contract declares an optional root `contractVersion` field:
|
|
28
|
+
|
|
29
|
+
- Type: string matching `^1\.[0-9]+\.[0-9]+$`.
|
|
30
|
+
- Semantics: the contract revision (major.minor.patch) the producer targeted.
|
|
31
|
+
- The major digit must equal the schema's `@vN` suffix.
|
|
32
|
+
- Absence means `1.0.0`.
|
|
33
|
+
- Producers should emit it; validators must not require it.
|
|
34
|
+
|
|
35
|
+
Schemas are not republished per revision. The `schema` consts stay `…@v1`;
|
|
36
|
+
each schema root carries a `$comment` naming the current revision (for
|
|
37
|
+
example `contract revision 1.1.0 (contract-stability epoch)`), which is the
|
|
38
|
+
machine-visible revision marker.
|
|
39
|
+
|
|
40
|
+
## Extension containers
|
|
41
|
+
|
|
42
|
+
Every object subschema that closes itself with `additionalProperties: false`
|
|
43
|
+
— the root and every nested closed object — declares an optional `ext`
|
|
44
|
+
property of type object. `ext` never appears in `required`. This placement is
|
|
45
|
+
uniform across all contracts and is enforced by the contract hygiene test, so
|
|
46
|
+
new contracts pick up the rule automatically.
|
|
47
|
+
|
|
48
|
+
`ext` is the only sanctioned place for data the contracts do not define.
|
|
49
|
+
Producers may add namespaced members; vendor-prefixed keys are recommended.
|
|
50
|
+
Contents are not validated at v1 and carry no authority over first-class
|
|
51
|
+
fields.
|
|
52
|
+
|
|
53
|
+
## Must-ignore rules
|
|
54
|
+
|
|
55
|
+
- Documents are closed at every level except `ext`. Unknown properties
|
|
56
|
+
outside `ext` are rejected — typo safety is a feature, not a bug.
|
|
57
|
+
- `ext` members must be ignored by consumers that do not recognize them, must
|
|
58
|
+
not alter the meaning of first-class fields, and must not be required to
|
|
59
|
+
interpret the document.
|
|
60
|
+
- Safety scanners are exempt from must-ignore. The dry-run validator's
|
|
61
|
+
mutation-intent, target-field, and source-reference scanners descend into
|
|
62
|
+
every nested object, including `ext`, and fail closed. A mutation flag or
|
|
63
|
+
an unsupported runtime target hidden inside `ext` is still a refusal;
|
|
64
|
+
must-ignore never suppresses a refusal.
|
|
65
|
+
|
|
66
|
+
## The widening ban
|
|
67
|
+
|
|
68
|
+
A v1 revision may tighten constraints or add semantics carried in `ext`;
|
|
69
|
+
anything that widens the accepted document space outside `ext` is a new
|
|
70
|
+
major. Widening includes adding enum values, opening a closed object, adding
|
|
71
|
+
optional first-class properties, and relaxing a pattern or a bound. The
|
|
72
|
+
compatibility gate turns this policy into a mechanical check: a widened
|
|
73
|
+
document fails validation under the baseline's validators.
|
|
74
|
+
|
|
75
|
+
## Deprecation policy
|
|
76
|
+
|
|
77
|
+
- Deprecating a field or value starts with a dated notice in the docs, at
|
|
78
|
+
least one minor revision before any behavioral change.
|
|
79
|
+
- Deprecated fields keep validating for their entire deprecation window.
|
|
80
|
+
- Removal — or any other change that rejects previously valid documents —
|
|
81
|
+
happens only at a major version.
|
|
82
|
+
|
|
83
|
+
## The compatibility gate
|
|
84
|
+
|
|
85
|
+
`scripts/check-contract-compat.mjs` (npm script `contract:compat`) validates
|
|
86
|
+
the current document corpus against the validators of a past release:
|
|
87
|
+
|
|
88
|
+
1. Resolve the baseline: a `--baseline <ref>` override, else the
|
|
89
|
+
`baselineTag` in `contracts/compat-baseline.json`. A null `baselineTag`
|
|
90
|
+
means the epoch is not yet tagged; the gate prints a notice and passes.
|
|
91
|
+
2. For each corpus entry, fetch the old schema from the baseline ref.
|
|
92
|
+
Contracts that do not exist at the baseline are reported as new since
|
|
93
|
+
baseline and skipped — new contracts are always admissible.
|
|
94
|
+
3. Compile the old schema and validate the current corpus against it: valid
|
|
95
|
+
fixtures, registered sample documents, and generated documents such as the
|
|
96
|
+
bundled readiness protocols. Generated documents are the ones most likely
|
|
97
|
+
to drift, so they are always included.
|
|
98
|
+
4. Any rejection names the fixture, the contract, and the baseline, and fails
|
|
99
|
+
the gate.
|
|
100
|
+
|
|
101
|
+
A self-consistency test runs the same plumbing with the working tree as its
|
|
102
|
+
own baseline, so the machinery is proven even while the gate is inert.
|
|
103
|
+
|
|
104
|
+
## Baseline procedure at release time
|
|
105
|
+
|
|
106
|
+
Cutting a release tag updates `contracts/compat-baseline.json` in the release
|
|
107
|
+
commit, setting `baselineTag` to the tag being cut, and runs
|
|
108
|
+
`npm run contract:compat` as part of the release checks. During the following
|
|
109
|
+
development cycle the gate therefore validates the evolving corpus against
|
|
110
|
+
the most recent release's validators. The first tag after the epoch is the
|
|
111
|
+
first baseline; before it exists, the gate stays inert.
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Distributions
|
|
2
|
+
|
|
3
|
+
A distribution is a branded wrapper package around `@mnstry/atelier`. It gives
|
|
4
|
+
a partner or program its own command name, display name, and look while the
|
|
5
|
+
MNSTRY Atelier package underneath keeps doing all of the work. This document
|
|
6
|
+
describes the model, the one invariant every distribution must hold, the
|
|
7
|
+
attribution requirement, and how to build one.
|
|
8
|
+
|
|
9
|
+
## The three-tier model
|
|
10
|
+
|
|
11
|
+
1. **Root package** — `@mnstry/atelier`. Owns every command, contract,
|
|
12
|
+
validator, and the bundled MNSTRY readiness pack. Published and versioned by
|
|
13
|
+
MNSTRY.
|
|
14
|
+
2. **Distribution wrapper** — a small npm package (for example Loomworks
|
|
15
|
+
Studio) that depends on the root package, ships a branded bin, and may add
|
|
16
|
+
extension packs, skills, and projection branding. It never modifies the
|
|
17
|
+
root package.
|
|
18
|
+
3. **End workspace** — what a user of the distribution initializes and works
|
|
19
|
+
in. Workspaces created through a distribution are ordinary Atelier
|
|
20
|
+
workspaces; nothing about them is fork-specific.
|
|
21
|
+
|
|
22
|
+
Because the wrapper is a dependency edge and not a fork, upgrading a
|
|
23
|
+
distribution is `npm install` of a newer root package, and every root gate
|
|
24
|
+
(contracts, boundary rules, egress checks) applies unchanged.
|
|
25
|
+
|
|
26
|
+
## The invariant
|
|
27
|
+
|
|
28
|
+
Add and rebrand, never alter root semantics.
|
|
29
|
+
|
|
30
|
+
A distribution may add commands of its own, add extension packs, add skills,
|
|
31
|
+
and restyle the projection. It must not remap or shadow root commands, replace
|
|
32
|
+
or edit bundled readiness protocols, or alter the bundled MNSTRY pack in any
|
|
33
|
+
way. Extension packs are the additive path: namespaced protocols load
|
|
34
|
+
alongside the bundled twelve, they never substitute for them. A wrapper that
|
|
35
|
+
changes what a root command means is a fork, not a distribution.
|
|
36
|
+
|
|
37
|
+
## Attribution requirement
|
|
38
|
+
|
|
39
|
+
Distributions must carry MNSTRY attribution. The normative wording of this
|
|
40
|
+
requirement lives in `TRADEMARKS.md` under "Required attribution" — this
|
|
41
|
+
document deliberately does not restate it, so the two cannot drift. See also
|
|
42
|
+
`docs/attestation.md` for how admission decisions are recorded.
|
|
43
|
+
|
|
44
|
+
Three related obligations live alongside it:
|
|
45
|
+
|
|
46
|
+
- **Quiet software.** Anything carrying the MNSTRY marks is bound by the
|
|
47
|
+
"Quiet software" clause in `TRADEMARKS.md`: attribution is a passive line,
|
|
48
|
+
never an upsell, interruption, or call-to-action.
|
|
49
|
+
- **Applications without a CLI.** An app powered by this package that ships no
|
|
50
|
+
CLI carries the attribution in its user-facing credits or about surface —
|
|
51
|
+
see "Applications" in `TRADEMARKS.md`.
|
|
52
|
+
- **NOTICE.** The package ships a `NOTICE` file at its root; under
|
|
53
|
+
Apache-2.0 section 4(d), every derivative redistribution must reproduce it.
|
|
54
|
+
|
|
55
|
+
Mechanically, attribution has three surfaces:
|
|
56
|
+
|
|
57
|
+
- **CLI output — automatic.** `runCli` renders the attribution line in
|
|
58
|
+
`--version` and `--help` output for any non-default brand. A wrapper that
|
|
59
|
+
dispatches through `runCli` does not need to print anything itself.
|
|
60
|
+
- **Distribution README — blocking.** The distribution's root `README.md`
|
|
61
|
+
must contain the exact byte string `powered by MNSTRY Atelier`. Surrounding
|
|
62
|
+
text is free.
|
|
63
|
+
- **Pack manifest — advisory.** An extension-pack manifest should declare
|
|
64
|
+
`"ext": { "mnstry.atelier/attribution": "powered by MNSTRY Atelier" }`.
|
|
65
|
+
The `mnstry.atelier/attribution` key is distinct from the `mnstry.atelier`
|
|
66
|
+
container used for projection branding.
|
|
67
|
+
|
|
68
|
+
`atelier distribution check` verifies the last two, plus a CLI probe: the
|
|
69
|
+
README byte string is blocking (exit 1 when absent), and when the target's
|
|
70
|
+
`package.json` declares a bin, the check **executes that bin** with
|
|
71
|
+
`--version` (spawned with the current Node, working directory set to the
|
|
72
|
+
target, real pipes) and requires the attribution in its output — this closes
|
|
73
|
+
the loophole where a wrapper injects no-op output streams into `runCli`.
|
|
74
|
+
Because the probe runs the target's code, only point it at distributions you
|
|
75
|
+
trust. A target that looks like a distribution (declares `@mnstry/atelier`,
|
|
76
|
+
ships `packs/`, or carries `atelier.project.json`) but declares no probe-able
|
|
77
|
+
bin — or whose `package.json` does not parse — is a blocking failure; only a
|
|
78
|
+
target with none of those markers gets an advisory skip. The manifest key is
|
|
79
|
+
reported but never blocks. Run it from the distribution root, or point it
|
|
80
|
+
elsewhere with `--target DIR`; `--pack DIR` overrides the default
|
|
81
|
+
`packs/*/atelier.pack.json` manifest lookup. Exit codes: 0 checks passed,
|
|
82
|
+
1 blocking attribution failure, 2 usage error.
|
|
83
|
+
|
|
84
|
+
## Building a distribution: the Loomworks walkthrough
|
|
85
|
+
|
|
86
|
+
The reference distribution, Loomworks Studio, lives at
|
|
87
|
+
`examples/loomworks-studio/` in this repository; the steps below are the
|
|
88
|
+
complete recipe it follows, and `npm run distribution:smoke` runs that recipe
|
|
89
|
+
against a packed tarball on every publish. Paths are relative to the wrapper
|
|
90
|
+
package root, for example `~/workspace/loomworks-studio`.
|
|
91
|
+
|
|
92
|
+
### 1. Create the wrapper package
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"name": "loomworks-studio",
|
|
97
|
+
"private": true,
|
|
98
|
+
"type": "module",
|
|
99
|
+
"version": "0.1.0",
|
|
100
|
+
"bin": { "loomworks": "bin/loomworks.mjs" },
|
|
101
|
+
"dependencies": { "@mnstry/atelier": "file:../.." }
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
A published distribution depends on a released `@mnstry/atelier` version
|
|
106
|
+
instead of a `file:` path.
|
|
107
|
+
|
|
108
|
+
### 2. Write the branded bin
|
|
109
|
+
|
|
110
|
+
The whole wrapper story is one small file:
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
#!/usr/bin/env node
|
|
114
|
+
import { runCli } from '@mnstry/atelier/cli'
|
|
115
|
+
import { createRequire } from 'node:module'
|
|
116
|
+
const { version } = createRequire(import.meta.url)('../package.json')
|
|
117
|
+
process.exit(await runCli({
|
|
118
|
+
argv: process.argv.slice(2),
|
|
119
|
+
brand: { command: 'loomworks', displayName: 'Loomworks Studio', version },
|
|
120
|
+
}))
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
With that in place, `loomworks --version` prints
|
|
124
|
+
`Loomworks Studio 0.1.0 — powered by MNSTRY Atelier <root version>` and
|
|
125
|
+
`loomworks --help` opens with the display name and the attribution line — both
|
|
126
|
+
rendered by `runCli`, neither maintained by the wrapper.
|
|
127
|
+
|
|
128
|
+
### 3. Put the attribution line in the README
|
|
129
|
+
|
|
130
|
+
Add the exact byte string `powered by MNSTRY Atelier` near the top of the
|
|
131
|
+
wrapper's `README.md`. This is what `distribution check` blocks on.
|
|
132
|
+
|
|
133
|
+
### 4. Brand the projection
|
|
134
|
+
|
|
135
|
+
Workspace projections read
|
|
136
|
+
`ext["mnstry.atelier"].distribution` from `atelier.project.json`:
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"ext": {
|
|
141
|
+
"mnstry.atelier": {
|
|
142
|
+
"distribution": {
|
|
143
|
+
"name": "Loomworks Studio",
|
|
144
|
+
"eyebrow": "studio projection",
|
|
145
|
+
"theme": { "accent": "#7a9e7e" }
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The "MNSTRY Tenant Readiness" section heading in the rendered projection stays
|
|
153
|
+
as is: it names the bundled MNSTRY pack.
|
|
154
|
+
|
|
155
|
+
### 5. Add an extension pack
|
|
156
|
+
|
|
157
|
+
Branded protocols live under `packs/<pack-name>/` with a manifest
|
|
158
|
+
(`atelier.pack.json`, schema `mnstry-atelier-extension-pack@v1`) whose
|
|
159
|
+
protocol ids are namespaced, for example `loomworks.readiness:open-intake`.
|
|
160
|
+
The manifest carries the advisory attribution key:
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"ext": { "mnstry.atelier/attribution": "powered by MNSTRY Atelier" }
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Extension protocols load alongside the bundled twelve; they never replace
|
|
169
|
+
them. Note that pack contributions to the tenant readiness packet ride in an
|
|
170
|
+
unschema'd runtime document (`mnstry.tenant-readiness-packet@v1`) whose shape
|
|
171
|
+
is pinned by tests, not by the versioned contracts the compat gate checks.
|
|
172
|
+
|
|
173
|
+
Declared pack paths resolve from the directory holding `atelier.project.json`
|
|
174
|
+
and may not contain `..` segments, so the pack directory has to sit inside the
|
|
175
|
+
config directory. Put the workspace config wherever the packs are, not one
|
|
176
|
+
level above them — `examples/loomworks-studio/` keeps both at the wrapper
|
|
177
|
+
root for exactly this reason.
|
|
178
|
+
|
|
179
|
+
### 6. Ship skills
|
|
180
|
+
|
|
181
|
+
Distribution skills follow the root package's dual-tree convention:
|
|
182
|
+
`skills/claude/<skill-name>/SKILL.md` and a byte-identical
|
|
183
|
+
`skills/codex/<skill-name>/SKILL.md`.
|
|
184
|
+
|
|
185
|
+
### 7. Verify
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
loomworks distribution check
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Exit 0 means the README attribution is present; the output also reports
|
|
192
|
+
whether each pack manifest declares the advisory attribution key. The repo's
|
|
193
|
+
distribution smoke gate runs the same check against the installed example.
|
|
194
|
+
|
|
195
|
+
## `ext["mnstry.atelier"].distribution` reference
|
|
196
|
+
|
|
197
|
+
All fields are optional; absence renders the default MNSTRY Atelier styling.
|
|
198
|
+
|
|
199
|
+
| Field | Type | Meaning |
|
|
200
|
+
| --- | --- | --- |
|
|
201
|
+
| `name` | string | Display name used in the projection title and eyebrow. |
|
|
202
|
+
| `eyebrow` | string | Second half of the eyebrow line (default `local projection`). |
|
|
203
|
+
| `theme` | object | Color overrides, keyed by theme token name. |
|
|
204
|
+
|
|
205
|
+
Theme token names: `background`, `surface`, `text`, `accent`, `eyebrow`. Each
|
|
206
|
+
maps to a CSS custom property (`--atelier-bg`, `--atelier-surface`,
|
|
207
|
+
`--atelier-text`, `--atelier-accent`, `--atelier-eyebrow`).
|
|
208
|
+
|
|
209
|
+
Hex-only rule: every theme value must be a CSS hex color of 3, 4, 6, or 8
|
|
210
|
+
digits (`#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`), and unknown theme keys fail
|
|
211
|
+
the build rather than silently dropping. Values are interpolated into a style
|
|
212
|
+
block, so non-hex input fails the build fast — this is an injection guard, not
|
|
213
|
+
a stylistic preference.
|
|
214
|
+
|
|
215
|
+
## Admission
|
|
216
|
+
|
|
217
|
+
A distribution changes branding, not authority. Admission of any payload into
|
|
218
|
+
a MNSTRY destination is decided and recorded exactly as for the root package —
|
|
219
|
+
see `docs/attestation.md` for the admission model and how attestations record
|
|
220
|
+
those decisions.
|