# One kind of edge A design, not an account of what exists. [`analysis.md`](analysis.md) measured the current catalogue and its value here is two lessons rather than its machinery: a field that means *depends on* should say so, and placement does not belong in a manifest. ## The idea **A module provides names. A module requires names. That is the only edge.** Everything the effort listed as separate declarations turns out to be one relation with different kinds of name on either end. ``` postgres provides postgres, database lavinmq provides lavinmq, message-bus kitty provides kitty, terminal anthropic provides anthropic, ai-assistant meshboard requires database, message-bus vscode requires terminal, display-server ``` A name is either **concrete** — a module's own name, so `requires: postgres` means that module and no other — or **abstract**, so `requires: database` means whatever provides it. The difference is not in the schema; it is whether more than one thing offers the name. That single relation absorbs three things the effort had listed separately: | Was going to be | Is | |---|---| | requires another module | requires a concrete name | | requires a resource — a database, a bucket | requires an abstract name | | an interface, with adapters providing it | an abstract name several modules provide | The interface stops being a kind of module. It is a name with more than one provider, and nothing has to declare that it is one. ## The node provides names too The move that makes capabilities stop being a separate system. A node's profile is a set of provided names. `display-server`. `container-runtime`. `amd64`. A module requiring `display-server` is satisfied by **the node**, exactly as a module requiring `database` is satisfied by another module. So there is one resolution rather than two: *is this name provided by anything available here?* A graphical application cannot be installed on a node with no display server for the same reason, and through the same code, that it cannot be installed without its libraries. And it means the host's capability detection — which already reports what a machine can be asked to do, with a reason for each verdict — **is the node's provides-list**. It was built to be read by a person; it turns out to be an input to resolution. ## What a module declares ```yaml name: meshboard provides: [meshboard] requires: [database, message-bus, container-runtime] excludes: [] runs: # what applying it means - container: ... constraints: # what must be true of a node that runs it - architecture: amd64 ``` **`excludes`** is the one genuinely new relation: naming something that cannot coexist with this. It is not derivable from requires and provides, and without it two modules that both provide `message-bus` look interchangeable when installing both would break the machine. **`constraints` are not placement.** They say what must be true of a node, never which node. Which node runs what is an inventory decision, and the measurement found the current catalogue deciding it in the manifest — a module pinning its database to a named node, so that a second node cannot provide it without editing the module that consumes it. That is the mistake this separation exists to avoid. ## What it deletes The question the effort opened with, answered for the design rather than for what exists. - **The distinction between requiring a module and requiring a resource.** One relation. - **The interface as a kind of thing.** A name with several providers. - **Capability checking as a separate mechanism.** The node is a provider. - **Domain grouping** ([ADR 0017](../../02-DECISIONS/0017-modules-outside-the-core-are-grouped-by-domain.md)). Folders assert relationships; edges record them. A domain is then a *query* over the graph — what changes together — rather than a directory somebody has to keep true. - **Tiers as a separate concept**, possibly. A tier is a level in the graph, and levels are computed. Whether the coarse boundary is still worth naming is below. ## What it does not delete, and should not **A resolver still has to exist**, and it will need version constraints, conflict handling and a story for when two modules provide one name and nothing says which to use. That is the long-solved and easily-botched part, and the design should say what it **delegates** to the platform's package manager rather than reimplementing. ## Open - **When two modules provide one name, who chooses?** The consumer names `database` and gets something. Whether the choice is a node setting, a mesh setting, or an explicit pin is the substance of the interface idea, and this proposal does not settle it. - **Are tiers a view of the graph, or a boundary that survives it?** If tiers are levels, the concept is derived and the word is a convenience. If the tier rule — *a tier may depend only on tiers below it* — is meant to be enforceable, it is a constraint on the graph rather than a description of it, and it has to be stated as one. - **Does a name need a type?** `database` and `terminal` are both abstract names, and a consumer of the first needs credentials while a consumer of the second needs a command to run. Whether that difference lives in the name, in a type beside it, or in what the provider hands back is undecided. - **What does a node provide that is not a capability?** Its architecture is a provided name under this scheme, and so is its operating system. That may be elegant or may be one abstraction too far; nothing here tests it.