Drop the invented layer taxonomy from OVERVIEW.md

The overview had been restructured around a named three-layer model —
user, developer, filesystem — that was never part of the design. The
underlying point stands: a reader must be able to tell whether a given
statement applies to them, and a code-level symbol must never appear in
prose without saying whose vantage point it belongs to.

Sections are now named for the reader they address rather than for a
tier: "Running the dpm binary", "Writing a program against libdpm-core",
"What DPM reads and writes on disk". The remaining uses of "layer" are
the capability-growth layers the design already defines.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-10 00:48:47 -04:00
parent c41adc2498
commit 298f4d5afe

View File

@@ -25,17 +25,7 @@ libdpm-core implements no package operations. It routes and hosts.
Everything passes through libdpm-core: binary-to-module, module-to-module, program-to-module. No consumer performs module discovery or dynamic loading itself.
## Three layers to read this at
DPM is met at three different layers, and which one you are at determines what any statement below means for you.
- **The user layer** — a person at a shell running the dpm binary. Everything here is a command and its flags.
- **The developer layer** — a program written against the C API in `<dpm/core.h>`, linking `-ldpm-core`. Everything here is a function call. Module authors live here too, on the other side of the same boundary.
- **The filesystem layer** — the directories and files DPM reads and writes: configuration, the module directory, the target root, and the installed artifacts.
The sections that follow are labelled by layer. Where a mechanism exists at more than one layer, it is described once at each, because the same idea looks different depending on where you stand.
## How it works at the user layer
## Running the dpm binary
The dpm binary's subcommand surface is exactly the set of loadable modules. `dpm <module> <command> [args...]` loads that module and hands the command to it; `dpm <module> help` asks the module to describe itself. There is no fixed list of operations baked into the tool, because the tool contains no capability logic — it parses arguments and prints.
@@ -50,7 +40,7 @@ Four flags redirect the system defaults:
These four exist because of a rule the design imposes on itself: every field a linked program can override must also be a flag, so anything reachable from code is reachable from a shell. That rule holds for any override added in the future.
## How it works at the developer layer
## Writing a program against libdpm-core
A program links libdpm-core and includes `<dpm/core.h>`. Everything it does happens through a **context**, an opaque handle obtained from `dpm_open`.
@@ -73,7 +63,7 @@ The context owns everything it hands out. Every string a caller receives stays v
**Services.** A module reaches libdpm-core through the context that dispatched the call: `dpm_log` to write a message, `dpm_config_get` to read a value from its own configuration namespace, `dpm_module_path` to learn where modules live, `dpm_core_version` to learn the library's version. A module needs no file handling and no logging machinery of its own.
## How it works at the filesystem layer
## What DPM reads and writes on disk
**Configuration** lives in `/etc/dpm/conf.d/`. Each `.conf` file in that directory is one namespace named after the file: `core.conf` holds the library's own settings, and a module named `mymodule` reads `mymodule.conf`. Files are sectioned, and a value is addressed by namespace, section, and key. The library's own file carries the log level, whether to write a log file and where, and the default module directory.
@@ -94,7 +84,7 @@ The context owns everything it hands out. Every string a caller receives stays v
## The module contract
*Developer layer, module-author side.*
*What a module author implements.*
A module is one `.so` in the module directory exporting five reserved symbols: a command entry point, its own version, a one-line description, the minimum libdpm-core version it supports, and a manifest. The manifest declares the module's entire functional surface — for each API, its name, table version, and the exact exported symbol carrying the table.
@@ -112,7 +102,7 @@ libdpm-core validates a module completely before offering it to anyone, in five
A module failing any step is refused with an itemized reason and its library handle closed. Validation is all-or-nothing: if a handle comes back, the contract already passed. Consumers never defend against partially valid modules, because they cannot receive one.
At the user layer this is visible as a module missing from `--list-modules` with a logged reason, or as an itemized failure when the module is named directly.
From the command line this shows up as a module missing from `--list-modules` with a logged reason, or as an itemized failure when the module is named directly.
## Versioning
@@ -134,7 +124,7 @@ Each layer of the system installs the dependencies of the next using only what a
### Because one implementation must serve every caller
The requirement that every implementation exist exactly once, and be consumable by the dpm binary, by other layers, and by external programs, is what makes libdpm-core a C ABI library rather than an application with a library carved out of it. This is also why the user layer and the developer layer stay in step: the flags are the override fields, one for one, so a developer and a user redirect the same things by the same names.
The requirement that every implementation exist exactly once, and be consumable by the dpm binary, by other layers, and by external programs, is what makes libdpm-core a C ABI library rather than an application with a library carved out of it. It is also why the command line and the C API stay in step: the flags are the override fields, one for one, so a program and a person redirect the same things by the same names.
### Because nothing may be trusted that has not been verified