Revise the proposed engine rule so a store may serve several engines.

An engine enables one capability through a consistent interface. It is not the store's only client and it is not an access-control mechanism. ADR-015 stays proposed.

Assistant: grok
Assistant-Session: 01a1609f-72b8-7222-9dff-fae300115e42
This commit is contained in:
tegwick 2026-10-22 14:20:02 +02:00
parent ae75aa4376
commit 87787807fa
2 changed files with 98 additions and 59 deletions

View file

@ -107,30 +107,34 @@ Two boundaries are lexical as well as architectural:
### 2.2 Engine
An engine is the deterministic processor for one domain, as proposed in
[ADR-015](docs/adr/ADR-015-engine-is-a-processor.md). The canon states the
domain. The engine implements it. Tooling holds the records. The engine owns
the record type and is the only admitted client of its store.
An engine is a tool of automation for one capability, as proposed in
[ADR-015](docs/adr/ADR-015-engine-is-a-processor.md). It encapsulates the
complexity of that capability in a consistent, often simpler interface than
doing the work by hand. The same inputs yield the same result, which reduces
failure modes and makes the work more reliable and more efficient. The canon
states the domain. The engine implements the capability. Tooling holds the
records. Several engines may use one store, each for a different capability.
One library has three manifestations. The library performs the domain
operations. The CLI runs that library in a process. A service is the same
process kept running for one scoped environment. At start, that process is
injected with the credential for that environment alone. The credential is how
the process reads its inputs and writes its outputs. Callers do not receive it.
An engine enables that capability. It is not an access-control mechanism.
Restricting who may act remains access-engine's domain.
One library has three manifestations. The library performs the capability.
The CLI runs that library in a process. A service is the same process kept
running for one scoped environment. At start, that process is injected with
the credential for that environment alone. The credential enables the
capability. Callers do not receive it.
An operator is a Staff actor who starts or calls an engine. Authentication
establishes who the operator is. Permission to use one named instance, or a
named set of instances, is an access rule rendered by access-engine. The
engine enforces that result and does not decide it. Holding the use-privilege
does not confer the injected credential, and it does not authorize every
operation the domain can perform. The engine still applies its deterministic
rules to each call.
named set of instances, is an access rule rendered by access-engine. Holding
that use-privilege does not confer the injected credential. The engine does
not decide the permission and does not become a gate on the store.
An operator who is allowed to delegate may assign a task to another operator
who is allowed to use an engine the delegator cannot use. The delegate uses
the engine. The delegator does not gain that use-privilege or the injected
credential. Engines stay stringent. Operators stay flexible. An operator's
reliability does not relax an engine check.
credential. Engines stay deterministic. Operators stay flexible and are
expected to be reliable in the work they choose and assign.
Until ADR-015 is accepted, this section records the proposed doctrine. The
security-layer standard is amended only after acceptance.
@ -366,9 +370,11 @@ not cause another component to invent authority or silently substitute a
broader credential.
The credential injected into an engine instance is that instance's
encapsulated privilege, defined in §2.2. It authorizes the process toward its
store for one scoped environment. It is not the operator's authority to use
the instance, and it is not issued to the operator.
encapsulated privilege, defined in §2.2. It enables that capability against
its store for one scoped environment. Another engine may hold a different
credential to the same store for a different capability. The injected
credential is not the operator's authority to use the instance, and it is not
issued to the operator.
## 10. MCP and tooling doctrine
@ -742,7 +748,7 @@ return path.
| ADR-010 | Still meaningful — Autonomous mandate model. |
| ADR-013 | Still meaningful — MCP authorization integration. |
| ADR-014 | Still meaningful — policy self-modification protection. |
| ADR-015 | Proposed — an engine is a processor; an operator's use-privilege is separate from the credentials injected into the instance. |
| ADR-015 | Proposed — an engine enables one capability over Tooling; several engines may share a store; an operator's use-privilege is separate from the credentials injected into the instance. |
Future ADRs in Gate House decide doctrine and responsibility boundaries. They
must not select an Engine's implementation technology or create a Gate House

View file

@ -3,6 +3,7 @@
**Repository:** gate-house
**Status:** proposed
**Date:** 2026-10-22
**Revision:** 2026-10-22 — a store may serve several engines; an engine enables a capability and is not an access-control mechanism.
## Context
@ -21,28 +22,44 @@ separate use-privilege, "the operator uses the engine" collapses into "the
operator receives the engine's credentials," or into the engine deciding for
itself who may call it.
A first draft of this ADR also made each engine the only client of its store.
That rule is withdrawn. One store can back several capabilities, and each
capability can be an engine. Exclusive clientship would turn every engine into
a gate on the records.
## Decision
An engine is a deterministic processor for one domain. It implements that
domain from its canon. It does not hold the domain's records. Tooling holds
the records. The engine owns the record type: the schema, the invariants, and
the legal transitions. It is the only admitted client of its store.
An engine is a tool of automation for one capability. It encapsulates the
complexity of that capability in a consistent, often simpler interface than
doing the work by hand. The same inputs yield the same result. That
determinism is what reduces failure modes and what makes the work more
reliable and more efficient.
An engine implements its capability from the domain canon. It does not hold
the domain's records. Tooling holds the records. Several engines may use one
store. Each engine provides a different capability over those records. Being
an engine does not make a process the store's only client, and it does not
make the engine an access-control mechanism. An engine enables the capability.
Restricting who may act remains the domain of `access-engine`, not a property
of engines in general.
The implementation has three manifestations of one library:
1. **Library.** The domain operations: validate, explain, plan, and compute
the next state. A caller may use these pure operations without receiving a
store credential.
1. **Library.** The capability: validate, explain, plan, and compute the next
state. A caller may use these operations without receiving a store
credential.
2. **CLI.** The library in a process. At start, the process is injected with
the credential for one scoped environment. That credential lets the process
read its inputs and write its outputs. It is not returned to the caller.
reach the inputs and outputs of its capability. It is not returned to the
caller.
3. **Service.** The same process, kept running for a scoped environment that
needs a standing processor. A service is not a second implementation and
does not receive the credentials of any other environment.
`secrets-engine` is the reference shape. OpenBao is the store. The engine is
the lifecycle processor. Build, test, and production are different scoped
environments and different processes.
`secrets-engine` is the reference shape. OpenBao is the store. The engine
enables credential lifecycle as a reliable operation. Another engine may use
OpenBao for a different capability. Build, test, and production are different
scoped environments and different processes.
### Authentication and the operator
@ -53,16 +70,15 @@ not grant use of an engine.
Two privileges stay distinct.
- The **encapsulated privilege** is the credential injected into one engine
instance at start. It is how that process reads its inputs and writes its
outputs. The operator does not possess it, see it, or reuse it outside the
process.
instance at start. It enables that instance to perform its capability. The
operator does not possess it, see it, or reuse it outside the process.
Holding it inside the process does not turn the engine into a restriction
on the store.
- The **use-privilege** is permission to start and call one named engine
instance, or a named set of instances. `access-engine` renders that
permission. The engine enforces the rendered result and does not decide it.
A use-privilege is not a grant of the encapsulated credential, and it is not
permission for every operation the domain can perform. The engine still
applies its own deterministic rules to each call, including any decision
record that domain requires.
permission. The engine does not decide it and does not become the access
control for its store. A use-privilege is not a grant of the encapsulated
credential. It is permission to use the enabling interface.
Starting an instance and calling it both require a use-privilege for that
instance. The operator who starts it is the operator under whose use the
@ -76,22 +92,24 @@ uses the engine. The delegator does not gain the use-privilege and does not
gain the encapsulated credential.
Delegation assigns work. It does not transfer either privilege. The delegate
must already hold the use-privilege. The assignment cannot widen what the
delegate is allowed to do, and it cannot authorize an operation the engine's
own rules refuse. Engines stay deterministic and stringent. Operators stay
flexible. Reliability of an operator is a Staff expectation. It does not relax
an engine check.
must already hold the use-privilege. Engines stay deterministic, so the
enabled operation stays consistent. Operators stay flexible and are expected
to be reliable in the work they choose and assign. That reliability is a
Staff expectation. It is not a second restriction inside the engine.
## Consequences
- An engine intent that says the running service is the store of record is
aligned later: the database, the vault, or the log is Tooling, and the
engine remains the only admitted client and the owner of the record type.
aligned later: the database, the vault, or the log is Tooling. The engine
is a client of that store for its capability. Other engines may be clients
of the same store for other capabilities.
- `user-engine`, `tenant-engine`, `approval-engine`, and the access decision
log are processors over records in Tooling. Approval consumption remains one
transaction that the engine opens against its store. A user or tenant fact
remains readable only through the engine.
- `audit-core` appends and verifies. The tamper-evident store is Tooling.
log are enabling processors over records in Tooling. Another engine may use
the same records for a different capability. Approval consumption remains
the approval capability's own transaction. It is not an exclusive lock on
the store.
- `audit-core` enables append and verification. The tamper-evident store is
Tooling.
- A Deployment exists when a scoped environment keeps the process running. A
one-task environment manifests the same binary as a CLI. Absence of a
Deployment does not make the engine incomplete.
@ -105,18 +123,33 @@ an engine check.
### The engine holds the records
Rejected because the layer cut already gives persistence to Tooling. Putting
the store inside the engine makes the process the system of record and invites
every other engine intent to do the same.
the store inside the engine makes the process the system of record.
### Exactly one engine is the client of a store
Rejected because one store can back several capabilities. Each capability is
an engine. Exclusive clientship would block a second engine from offering a
different interface over the same records, and it would make the first engine
a gate.
### The engine is the access control for its store
Rejected because an engine enables a capability. It encapsulates complexity
so the operation is consistent, reliable, and efficient. Restricting who may
act is `access-engine`'s domain. An injected credential does not create a
second restriction mechanism.
### The operator receives the encapsulated credential
Rejected because the operator could then bypass the processor and call Tooling
directly. That is the failure the use-privilege exists to prevent.
Rejected because the operator would then perform the complex operation by
hand. The engine exists so that work goes through the reliable interface.
The use-privilege names who may invoke that interface. It does not hand over
the store credential.
### The engine decides who may use it
Rejected because that decision is an access rule. An engine that renders it
becomes a second policy decision point.
takes on an access-control role this ADR withholds from engines in general.
### Delegation transfers the delegate's privileges to the delegator
@ -125,7 +158,7 @@ delegate already holds the use-privilege. The delegator remains without it.
### Library only, or service only
Rejected because the domain implementation, the process boundary, and the
standing manifestation are one implementation. Collapsing them either gives
callers the store credential inside a library, or forces a Deployment for a
one-task environment.
Rejected because the capability, the process boundary, and the standing
manifestation are one implementation. Collapsing them either gives callers
the store credential inside a library, or forces a Deployment for a one-task
environment.