Florete

Decomposition

A0's units — the crates whose APIs humans author — and the integration generated around them

A0's code divides into units — crates in flor whose public APIs humans author before either line implements them — and the integration generated around them in flor-dist, two SD crates among it. This page says which code is a unit, why, on which line A0 ships each, what must hold across all of it, and what binds the code that is not a unit; the rules it applies are AI-First Development's, and the behaviour it divides is designed in A0's Design, Installer and Artifact Store, and in C0's Identity & Naming and Enrollment, which A0 builds on.

Everyone starts at the Shape: it maps the units with the line A0 ships each on, the SD crates and the rest of the integration around them, and the rules that hold across both, the obligations among them. If you are authoring a unit's API, the obligations it touches are yours to keep, and how you keep each is recorded beside it. If you are confirming a unit's line or changing the decomposition, the Decision Log says why each part is as it is. How far each unit's code has come, and what its work is estimated at, live in the tracker, and which line supplies a unit now in flor-dist's assembly — not here.

Shape

STRUCTURE

  subject     A0's code as units — crates in flor whose public API a human authors before either
              line implements it — each on the line A0 ships it on; the integration outside them,
              bound by the corpus and the suite; and what must hold across both

  units       crate                       owns                                                line
              flor-identity               names and name rules; certificate checks;           DD
                                          issuance and signing; the import check
              flor-identity-store         every file of the identity store, the               SD
                                          authorities' included
              flor-artifact               signed documents: the envelope, the canonical       DD
                                          form, signing, verification, the admission checks
              flor-artifact-store         the artifact store over its file backend, the       SD
                                          freshness mark included
              flor-logging                the log sink, framing, line format and sanitising;  DD
                                          the error report
              flor-vertex-config          the vertex's configuration family, the ingress      DD
                                          rules among it
              flor-vertex-authz           the access check: admit or deny by the target's     DD
                                          rules; the egress filter, advisory
              flor-vertex-transport       QUIC over one UDP socket, mTLS both ways, the SNI   DD
                                          registry, the resolver
              flor-vertex-inbound-socks5  a SOCKS5 listener per initiator                     DD
              flor-vertex-outbound-tcp    admitted connections to their own targets'          DD
                                          upstreams

  SD crates   crate               owns                                                used by
              flor-dist-bundle    the bundle: container, manifest, entry-name check   rete, flor
              flor-dist-scope     a scope's root under the state directory, its lock  flor,
                                  and its receipt                                     flor-vertex

  integration generated in flor-dist, bound by the corpus and the suite
              each executable's own logic: rete, flor, flor-vertex
              the rete source, the validator, the compiler and its version ladder
              enroll's run, activation
              the systemd unit, its text a golden fixture

  layers      edges point down only
                1  flor-identity, flor-logging                   data
                2  flor-artifact                                 data
                3  flor-vertex-config, both stores               data
                4  flor-vertex-authz                             data
                5  flor-vertex-transport                         lifecycle
                6  the two adapters                              lifecycle
              a data crate never depends on a lifecycle crate
              only integration calls a store

FLOW

  runs        rete compile          flor-artifact-store reads the sets, the version ladder →
                                    compiler → flor-vertex-config validates → flor-artifact signs
                                    through flor-identity → flor-artifact's admission check →
                                    flor-artifact-store rebuilds with what was admitted
              rete issue-bundle     flor-artifact-store reads the set → flor-identity issues the
                                    principals → flor-dist-bundle packs them, the manifest signed
                                    through flor-artifact
              flor enroll           flor-dist-bundle opens the bundle, its manifest verified through
                                    flor-artifact against the scope's pin where it has one →
                                    flor-identity's import check → flor-artifact's admission check
                                    → both stores write what was admitted
              flor-vertex           both stores read → flor-vertex-config makes the ingress rules
                                    from the vertex's admitted document → transport, the access
                                    check, the adapters

RULES

  obligations what must hold across A0's code, and the units owning the checks it rests on; each
              holds despite the generated code around its check
                 must hold                              checked by
              1  nothing lands in a store that its      flor-identity,
                 check did not admit                    flor-artifact
              2  a pinned scope's trust domain, node    flor-identity
                 and anchors never change
              3  a pinned scope's bundle is verified    flor-artifact,
                 against its pin before anything is     flor-identity
                 written
              4  no set older than its node's mark is   flor-artifact
                 admitted
              5  no scope handle or store path comes    flor-identity
                 from a name the rules refuse
              6  the access check applies exactly the   flor-vertex-config
                 rules of the vertex's admitted
                 document; no document, no service
              7  every accepted connection passes the   flor-vertex-authz,
                 access check and reaches only its own  flor-vertex-transport,
                 target's upstream                      flor-vertex-outbound-tcp
              8  the access check judges only a peer    flor-vertex-transport
                 that mTLS authenticated
              9  authority keys stay in the operator's  flor-identity
                 store; no key's text reaches a log,
                 an error report or a stray file

  conventions errors as error_stack reports over the crate's own Error; no DI framework type in a
              unit's API; a unit runs inside a tokio runtime and never builds one; events through
              tracing, checked as behaviour

  names       flor-vertex-<part> for a vertex's parts, direction before protocol where the kind has
              one; flor-<concern> for other units; flor-dist-<name> for integration

Evolution sketches

Recorded so that nothing above forecloses them; each names what A0's boundaries owe.

  • C0's agent takes over the scope lock and the receipt. Owed: they sit in one SD crate, flor-dist-scope, which the agent takes up as it stands — a unit, renamed, if the agent goes DD.
  • C0's coordinator reads documents without a store. Owed: flor-artifact stands apart from flor-artifact-store.
  • C0's counters and flood-safe Layer, neither of which A0 builds. Owed: the closed event fields A0 keeps ready for both (DSGN-A0-18), which the suite holds as behaviour; and a subscriber, set up by flor-logging, that takes another crate's Layer, so the Layer can arrive SD first beside it.
  • C1's forwarding engine and connection manager arrive as candidates of their own, the vertex's integration regenerated around them. Owed: transport's UDP socket moves behind the connection manager then, a change to one contract; FlorIO arrives as flor-vertex-florio.
  • C1's ctrl plane joins the vertex's configuration family; if it starts as SD, then the flor-vertex-config unit will be split. Owed: extensibility for new configuration family.

Decision Log

Entries are grouped by what a change would touch.

Across the units

DECOMP-A0-01. Superseded by AIFIRST-31.

DECOMP-A0-02. Superseded by AIFIRST-32.

DECOMP-A0-03. Layer the units as the Shape draws them, and let only integration call a store. Units take what the stores yield as values. Because the stores serve executables, opening files under roots the executable chose; their API is no building block for libraries.

DECOMP-A0-04. State what must hold across A0's code, and record beside each obligation how it is kept. The Shape's obligations, each with the units owning the check it rests on. Whoever authors a unit's API records, in a third column of that list, how the unit keeps each obligation it touches — by the API, or by tests at the API; an obligation its SD line cannot keep is marked unkept there, naming the unit, with the call and its reason in this log. Because one column, read one way, shows what is kept and what is not, while a call's reason stays with the call. Rejected a mark on the obligation with a note under the list, one fact in two places and a reason in the Shape.

DECOMP-A0-05. Superseded by AIFIRST-30.

DECOMP-A0-22. Superseded by AIFIRST-31.

Identity and artifacts

DECOMP-A0-06. Split identity: flor-identity DD, flor-identity-store SD — the store owning every file, identity every use of a key. flor-identity holds names and name rules, certificate checks, the import check with its pin, and every use of a key — issuance and signing — handing no authority key to a caller; the one key it hands out is a new TLS principal's, for the bundle. flor-identity-store holds every file of the store, the keys at rest among them, the authorities' included. Because the store is new code while the security-critical parts exist or are small, and files are the store's concern while what is done with a key, and every check, is identity's. Accepting that the store stays SD only as far as its API's author can keep the obligations it touches (DECOMP-A0-04). Rejected one DD crate, which puts the store's new code on DD; identity writing its own key files, which splits the store's layout across two crates; authority keys handed to callers, custody by discipline.

DECOMP-A0-07. Split artifact: flor-artifact DD, flor-artifact-store SD, and signatures at the bytes. flor-artifact holds the envelope, the canonical form and signature block of any signed JSON — the envelope and the bundle manifest alike — signing and verification, and the admission checks; identity holds certificate checks, the key-type-to-scheme table and signing bytes. flor-artifact-store holds the store over its file backend, the mark included. Because the store is the largest new A0 piece and can start on SD while signatures stay DD, and C0's relay reads documents without a store. Documents take each new plane's churn, which may carry them to SD for a milestone, while identity's pin and authorities keep it on DD. Accepting that the store stays SD only as far as its API's author can keep the obligations it touches (DECOMP-A0-04). Rejected one unit on one line; signatures wholly in identity, which would parse JSON documents; wholly in artifact, apart from the certificates they judge.

DECOMP-A0-08. Keep the vertex's configuration a unit, flor-vertex-config, holding the ingress rules. The vertex configuration family, shared by the compiler and the vertex, and the ingress rules, which it alone makes. DD. Because payload models sit outside the documents layer, one per family (ART-A0-03), and what the access check enforces is made by reviewed code. Accepting that C1's ctrl payload churns here, and may land beside it as a unit of its own. Rejected inside flor-artifact, as ADR-0012's "one module" line has it; shared generated code; "payload" in the name, which needs the envelope's context.

DECOMP-A0-09. Superseded by DECOMP-A0-23.

The node

DECOMP-A0-10. Hold the access check and the egress filter in a unit of their own, flor-vertex-authz. The access check is a unit of its own, apart from transport and the adapters, with the egress filter beside it for every inbound. DD. Because the access check is AIFIRST-07's own case and the stable core, while transport is where C1's churn lands and may move to SD. Because the egress filter applies the vertex's rules in the other direction — one concern with the access check — and every inbound, this milestone's or a later one's, calls it. Accepting that the check decides on whatever peer transport authenticated; and that an advisory check shares the API obligations rest on. Rejected either check inside transport, which ties it to transport's line; in an adapter or the vertex's assembly, a check per path; SD at A0, for a check small enough to review; the name flor-vertex-acl, which reads as the rules rather than the check applying them — the rules are flor-vertex-config's, and "access check" is the identity model's term for authorization (ID-C0-16); the egress filter in an inbound, which every later inbound would need again, or in a unit of its own, an API for one membership test.

DECOMP-A0-11. Keep the transport endpoint and both adapters DD, reusing their working code. flor-vertex-transport, flor-vertex-inbound-socks5 and flor-vertex-outbound-tcp are units with their current code as DD bodies; the outbound holds dispatch. Transport stays UDP-bound in A0. Because the code works and SD cannot reuse it except through a contract (AIFIRST-12); each moves to SD on its own, by reverse substitution, when it needs substantial change. Rejected regenerating working code; the outbound's dispatch in generated code.

DECOMP-A0-12. Generate every executable's own logic, the vertex's and enroll's included. rete, flor and flor-vertex are integration in flor-dist; so are enroll's run and activation. Because every check an obligation rests on is a unit's, and every obligation must hold despite the glue (DECOMP-A0-04); and the vertex's glue is where C0 and C1 churn. Accepting that SD writes lifecycle code in A0, and that the repair path rests on the bundle's fixtures and the suite's convergence tests. Rejected flor-vertex as a binary-shaped DD unit, which rested on rules and dispatch sitting in its glue; enroll as a unit.

DECOMP-A0-13. Share the scope through an SD crate, flor-dist-scope, and leave the rest of the host and the systemd unit to generation. flor-dist-scope holds a scope's root under the state directory, its lock and its receipt, shared by flor and flor-vertex, its API and body the agent's. The state directory's upkeep and activation are flor's own logic. The layout is pinned by fixtures and the refusals held by the suite; the systemd unit's text is a golden fixture, reviewed when it changes. Because both executables derive every path from a scope and hold its lock, so one crate keeps one implementation of them (AIFIRST-06), which C0's agent takes over with only its call sites regenerated; no unit calls it, and no obligation rests on its API. Because a fixture reviews the text systemd reads. Accepting that the scope's paths sit in generated code, so that no path comes from a name the rules refuse has to hold through identity's API (DECOMP-A0-04). Rejected flor-host as a unit, an API no unit calls; one crate holding rete's tree too, a layout no other consumer shares; a reviewed crate for one substituted string.

Cross-cutting

DECOMP-A0-14. Keep logging a DD unit, with the error report inside it. flor-logging holds the sink, the framing, the line format and sanitising, and renders the report a command prints; errors themselves follow the conventions (DECOMP-A0-15), with no error unit. Because sanitising stands between peer text and an operator's terminal, half the code exists, and the two ways a process speaks to its operator belong together. Accepting that C0's flood-safe Layer may arrive on SD, beside it. Rejected logging as fixtures alone; an error crate for a style (AIFIRST-08).

DECOMP-A0-15. Fix four conventions for every unit's API, and leave the rest to each unit. Errors as error_stack reports over the crate's own Error; no DI framework type in a public API — the glue may use fundle or propose another crate, approved like any dependency (ADR-0017); a unit runs inside a tokio runtime and never builds one; events through tracing, checked as behaviour. Because every API author meets these, and a framework type in a contract ties every regeneration to its releases.

Outside the units

DECOMP-A0-16. Generate the compiler, and check its output with the suite's oracle. The rete source, the validator, the compiler and its version ladder are integration. Golden compile fixtures, read by generation, pin source to compiled tree; a property test in the suite, hidden from generation, checks that every ingress row's allow set equals the expansion of the source's roles and groups; flor-vertex-config validates before signing. Because the compiler carries A0's heaviest churn, its contracts are external, and the operator — not an adversary — writes its input. Accepting that its output decides what the access check admits, the suite's oracle — not the operator's diff — being the evidence for it. Rejected the compiler as a unit, by policy or by size.

DECOMP-A0-23. Generate the bundle as an SD crate, flor-dist-bundle. flor-dist-bundle holds the bundle — container, manifest, entries, the entry-name check — shared by issue-bundle and enroll's run, its API and body the agent's, with no crypto of its own: the manifest is signed and verified through flor-artifact. Golden bundles from each release pin its format, and the suite holds its refusals. Because one crate keeps one implementation of a format one executable writes and another reads (AIFIRST-06); no unit calls it, and the checks a pinned scope's bundle needs are flor-artifact's and flor-identity's, so its API is the agent's. Accepting that a regenerated bundle is safe only as far as its fixtures reach, on A0's only channel, the path every repair takes. Rejected a unit on SD for its format, which no API keeps; a DD unit, or a container inside flor-artifact, either pacing A0 by a body nobody has written; the entry-name check in enroll's run, a second spelling of the names the bundle writes.

DECOMP-A0-17. Retire the code no unit covers. At the split, flor keeps what becomes units and drops the rest — the compiler, loader and validator, the vertex's assembly, home resolution, the CLI mains; git history keeps it, and generation is not pointed at it. Because flor holds units, fixtures and the suite, and pre-A0 code in reach invites imitation of shapes the design has left. Rejected keeping it as reference material; contracting it because it is ready.

Lines, names and what follows

DECOMP-A0-18. Ship eight units on DD and two on SD. DD: flor-identity, flor-artifact, flor-logging, flor-vertex-config, flor-vertex-authz, flor-vertex-transport, both adapters. SD: flor-identity-store, flor-artifact-store. Because the cost of an error in each, its existing code and the churn ahead were weighed per unit. Accepting new DD code on A0's path: the access check, identity's import check and its key operations, artifact's signing and verification, logging's sink, the egress filter.

DECOMP-A0-19. Name a crate for what it is part of, not for who uses it. flor-vertex-<part> for a part of the vertex, whichever executables use it — its configuration among them, though rete writes it — direction before protocol where the kind fixes one; FlorIO, both ways and both north and south, as flor-vertex-florio; flor-<concern> for any other unit; flor-dist-<name> for integration, the executable's name in its bin target. An SD body keeps its unit's name; an SD crate is named as integration, and takes a unit's name if it becomes one. Because a name should say what a crate is part of without context, and many crates read grouped; and what a crate is part of stays put while the executables using it change. Rejected flor-<unit> throughout; a prefix chosen by who uses a crate; northbound- or bidir- for FlorIO.

DECOMP-A0-20. Superseded by AIFIRST-36.

DECOMP-A0-21. Record what later milestones need, and leave C0 its own page. Each need of C0 and C1 known now is an evolution sketch on this page, naming what A0's boundaries owe it; C0's units are drawn on C0's own page. Because each is known now and cheap to state. Rejected pre-drawing C0's units here.

On this page