The contract, explained
The component contract is the normative document: terse, numbered, written so an implementer, human or agent, never has to guess. This page is the tour for everyone else. It adds no rules; where the two disagree, the contract wins.
Five ideas
1. Two small input files, and one of them isn't ours.score.yaml says what your app needs: containers, ports, "I need a Postgres", "I need OpenObserve". That format is Score, a CNCF spec, so it never mentions a cloud and other tools can read it too. dotkit.yaml says where it runs: environments, provider, target. It is optional; with no file at all you get a local emulator. Keeping the two apart is what makes an app portable: the file developers edit has nothing cloud-specific in it.
2. A component is a promise, not a template. A component is versioned and pinned by digest, has declared inputs and outputs, and exists only when a conformance test says it works. You don't edit a component; you set its inputs. Three kinds: primitives (a network, a runtime, a bucket, a secret, a database), written once per cloud; composed services like OpenObserve, which are just a manifest saying "these containers, plus a bucket and a network", so they run on every cloud that has the primitives; and chart services for Kubernetes-only tools such as SigNoz.
3. Wiring is automatic, and secrets never travel in the clear. In score.yaml you write ${resources.obs.otlp_endpoint} and the generator resolves it to the real endpoint. If any part of a value is sensitive, the whole value is treated as a secret and reaches your container through the platform's secret mechanism, never as a plain environment variable. You don't have to know which outputs are sensitive; the contract does.
4. Plain OpenTofu underneath, generated, immutable. What DOTkit produces is ordinary OpenTofu you can read: one module call per component, wired together. Generated files are regenerated every time and never hand-edited, so upgrading a component is safe. Customisation goes through inputs or a small user-owned folder that the generator never touches. State lives in an encrypted remote backend; no state file ever sits on your laptop.
5. Nothing is claimed that hasn't run. Every component x cloud x target combination is a cell in a matrix, and a cell exists only after its conformance test passed: emulator-tested on every merge against MiniStack, cloud-tested nightly against a real account. The docs list only green cells. "Should work" is not a tier.
What this rules out, on purpose
- Editing generated HCL by hand (use inputs; eject is not a feature).
- A component that only works on one cloud pretending to be portable (
targets:is a claim, the matrix is the proof). - Cloud config smuggled into
score.yaml(annotations are never read). - An agent "fixing" the contract when a command fails: that's a reproduced blocker, filed as an amendment, decided by a human.
Reading the contract itself
Sections 2 to 6 are frozen: input files, component manifest, the normative module graph (§3b is the one to read if you want to see exactly what dotkit generate emits), provider profiles, secrets, environments and sequencing. Sections 7 onward (conformance, CLI, OpenObserve specifics, scope) evolve with the code. The changelog at the bottom records every amendment.

