Skip to content

Commit d67aba9

Browse files
committed
Plan 1.6 security model and modularity gates
1 parent 5dffee4 commit d67aba9

4 files changed

Lines changed: 301 additions & 0 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,8 @@ full release ladder.
475475
- [Roadmap](ROADMAP.md)
476476
- [Changelog](CHANGELOG.md)
477477
- [Versioning Plan](docs/versioning-plan.md)
478+
- [Modularity Policy](docs/modularity-policy.md)
479+
- [Runtime Facts And Policy Proofs](docs/runtime-facts-and-policy-proofs.md)
478480
- [Release Runbook](docs/release-runbook.md)
479481
- [Release Checklist](docs/release-checklist.md)
480482
- [Build, Containers, And Rootless Podman](docs/build-and-podman.md)

docs/modularity-policy.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Fluxheim Modularity Policy
2+
3+
Status: 1.6 planning policy
4+
5+
Fluxheim treats large files and unclear crate boundaries as security review
6+
risk, not only style debt. The 1.6 Pingora-exit line should move Fluxheim
7+
toward the same discipline used by smaller security-focused workspace projects:
8+
focused crates, small files, explicit adapters, and release-gated exceptions.
9+
10+
## Core Rule
11+
12+
The root `fluxheim` crate should become orchestration glue. Major domains
13+
belong in focused workspace crates with one-way dependencies back to common
14+
types, never circular dependencies through admin, proxy, or runtime modules.
15+
16+
Target crates include:
17+
18+
- `fluxheim-common`
19+
- `fluxheim-config`
20+
- `fluxheim-cache`
21+
- `fluxheim-load-balancer`
22+
- `fluxheim-web`
23+
- `fluxheim-php-fpm`
24+
- `fluxheim-compression`
25+
- `fluxheim-geoip`
26+
- `fluxheim-observability`
27+
- `fluxheim-protocol`
28+
- `fluxheim-snapshot`
29+
- `fluxheim-acme`
30+
- `fluxheim-headers` or `fluxheim-http-policy`
31+
- `fluxheim-runtime`
32+
- `fluxheim-server`
33+
- `fluxheim-proxy`
34+
- future `fluxheim-wasm`, `fluxheim-http3`, and ecosystem crates
35+
36+
## File Size Rule
37+
38+
New or newly split Rust implementation files should follow:
39+
40+
- normal target: 300 lines or less;
41+
- hard target: 500 lines or less;
42+
- tests should be split by behavior before they make a production file hard to
43+
review;
44+
- generated, vendored, or machine-owned files must be isolated and excluded
45+
from human-review line limits.
46+
47+
Existing large files are legacy debt. Do not block 1.6.0 on the current file
48+
sizes, but do not let them grow without a documented reason.
49+
50+
## 1.6 Migration Gate
51+
52+
`v1.6.0` should add a report-only gate that lists non-generated `.rs` files over
53+
500 lines and records the exception inventory. The gate should fail only for:
54+
55+
- new non-generated `.rs` files over 500 lines;
56+
- files that exceed 500 lines after being split below the threshold;
57+
- legacy exception files that grow without an exception update.
58+
59+
Each legacy exception should record:
60+
61+
```text
62+
Path:
63+
Reason:
64+
Owner:
65+
Split plan:
66+
Target release:
67+
```
68+
69+
By the end of the 1.6 line, the exception list should be materially smaller.
70+
After Pingora is gone and the root runtime is split, the gate can become a hard
71+
rule for all non-exempt files.
72+
73+
## Split Rules
74+
75+
Split by reason to change, not by arbitrary size alone.
76+
77+
Prefer modules such as:
78+
79+
```text
80+
src/
81+
|-- lib.rs
82+
|-- error.rs
83+
|-- types.rs
84+
|-- validate.rs
85+
|-- adapter.rs
86+
`-- subsystem/
87+
|-- mod.rs
88+
|-- state.rs
89+
|-- policy.rs
90+
|-- protocol.rs
91+
|-- storage.rs
92+
`-- tests.rs
93+
```
94+
95+
Avoid mixing parser code, validation, I/O, policy decisions, state mutation,
96+
telemetry, and tests in one file. Pure logic should move into crates that can
97+
be unit-tested without a running server.
98+
99+
## Security Rationale
100+
101+
Small files and focused crates make pentest and code review more reliable:
102+
103+
- reviewers can reason about one trust boundary at a time;
104+
- duplicated security controls are easier to spot;
105+
- fuzz and fixture coverage can target pure logic;
106+
- adapters to external crates stay narrow;
107+
- redaction, authority checks, and failure modes are local;
108+
- future Wasm and HTTP/3 work starts from clean domain APIs.
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# Runtime Facts And Policy Proofs
2+
3+
Status: 1.6 planning policy
4+
5+
Fluxheim should understand its own runtime decisions without becoming a
6+
database. The goal is a small, typed, bounded model for operational evidence:
7+
what happened, which policy made the decision, and which public/redacted reason
8+
can be shown to an operator.
9+
10+
This is inspired by two internal design patterns:
11+
12+
- Aesynx: no ambient authority, typed handles, explicit capability movement,
13+
bounded telemetry, fail-closed admission gates.
14+
- Skrifheim: canonical facts, worlds, policy decisions, redacted debug output,
15+
and deterministic proof objects.
16+
17+
Fluxheim should adopt the discipline, not the database engine.
18+
19+
## Non-Goals
20+
21+
This work must not put a database, graph engine, AI system, or unbounded audit
22+
store in the request hot path.
23+
24+
Runtime facts are not a replacement for logs, metrics, traces, or admin status.
25+
They are a typed internal shape that those systems can export safely.
26+
27+
## Core Concepts
28+
29+
### Runtime Fact
30+
31+
A runtime fact is a bounded statement about Fluxheim state or behavior.
32+
33+
Examples:
34+
35+
- config candidate loaded;
36+
- config reload promoted;
37+
- route matched;
38+
- route access policy denied;
39+
- GeoIP context unavailable;
40+
- auth-request allowed or denied;
41+
- rate limit delayed or rejected;
42+
- load-balancer backend selected;
43+
- backend ejected or restored;
44+
- cache object admitted, bypassed, purged, or served stale;
45+
- ACME certificate installed or rollback attempted;
46+
- admin mutation accepted or rejected.
47+
48+
Facts should carry enough metadata for diagnostics without leaking secrets:
49+
50+
- fact kind;
51+
- config or policy epoch;
52+
- vhost and route identifier where safe;
53+
- bounded decision reason;
54+
- source subsystem;
55+
- monotonic event id or timestamp;
56+
- redacted subject identifiers;
57+
- optional causal parent or request correlation id;
58+
- public classification of whether it is safe for logs, metrics, traces, or
59+
admin status.
60+
61+
### Policy Proof
62+
63+
Security-relevant decisions should move toward small proof objects:
64+
65+
```text
66+
decision = allow | deny | redact | defer
67+
reason = bounded enum
68+
policy_epoch = current config/runtime policy epoch
69+
input_count = bounded count
70+
output_classification = public | sensitive | secret-internal
71+
```
72+
73+
The proof is not verbose per-request logging. It is a typed result that keeps
74+
policy decisions consistent across access control, cache admission,
75+
load-balancer selection, admin mutation, auth-request, GeoIP, rate limiting,
76+
Wasm hooks, and future WAF behavior.
77+
78+
### Runtime World
79+
80+
Fluxheim already has implicit worlds:
81+
82+
- current running config;
83+
- reload candidate;
84+
- known-good snapshot;
85+
- rollback target;
86+
- runtime load-balancer state;
87+
- cache status;
88+
- admin-visible status.
89+
90+
The 1.6 line should start treating those as explicit versioned views with
91+
epochs and clear promotion rules. A candidate config should not become current
92+
state without validation evidence. Runtime mutations should record the policy
93+
epoch they were made under.
94+
95+
## 1.6 Adoption Plan
96+
97+
`v1.6.0` should document the initial runtime fact kinds, policy-proof shape,
98+
redaction levels, and epoch terminology. It should not change request behavior.
99+
100+
Early 1.6 releases should introduce small types in a focused crate or
101+
`fluxheim-common` if the shape is still tiny:
102+
103+
- `RuntimeFactKind`
104+
- `RuntimeDecisionKind`
105+
- `RuntimeDecisionReason`
106+
- `PolicyProof`
107+
- `PolicyEpoch`
108+
- `RuntimeFactVisibility`
109+
110+
Mid 1.6 releases should route selected decision paths through proof objects
111+
while replacing Pingora boundaries:
112+
113+
- route access policy;
114+
- GeoIP allow/deny behavior;
115+
- rate limiting;
116+
- auth-request;
117+
- cache admission and origin protection;
118+
- load-balancer backend selection/ejection;
119+
- admin runtime mutations.
120+
121+
Later 1.6 releases may add a bounded in-memory fact ring for admin diagnostics.
122+
The ring must be optional, fixed-size, redacted by type, and safe to disable.
123+
It must never be required for request forwarding.
124+
125+
## Security Rules
126+
127+
- Runtime facts do not create authority.
128+
- Policy proofs are deterministic and bounded.
129+
- Sensitive identifiers are redacted at type boundaries, not manually at each
130+
log site.
131+
- Metrics stay low-cardinality.
132+
- Admin views expose bounded reasons, not raw secrets or private addresses in
133+
privacy-mode builds.
134+
- AI or Wasm consumers may only receive facts explicitly granted by policy.
135+
- Derived or advisory facts are never authoritative unless promoted through a
136+
deterministic path.
137+
- If fact collection fails, request handling must keep its configured
138+
fail-open/fail-closed behavior and record only a bounded diagnostic if safe.
139+
140+
## Why This Matters
141+
142+
Fluxheim has grown beyond a simple proxy. It now has cache, load balancing,
143+
admin mutation, snapshots, ACME, stream/UDP work, metrics, traces, and future
144+
Wasm/WAF/HTTP3 plans. A typed runtime-fact model lets operators and reviewers
145+
ask:
146+
147+
- why was this request denied?
148+
- why did this backend stop receiving traffic?
149+
- why was this object not cached?
150+
- which config epoch made this decision?
151+
- which admin action changed this pool?
152+
- which facts are safe to export?
153+
154+
That makes Fluxheim more self-aware and makes pentest review more local,
155+
without turning the proxy into a database.

docs/versioning-plan.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2389,6 +2389,20 @@ extractions: proxy and runtime move only when the native HTTP/server runtime is
23892389
ready, and admin moves only after domain crates expose stable APIs so the admin
23902390
crate does not become a circular dependency hub.
23912391

2392+
The 1.6 line also adopts two security-engineering policies learned from the
2393+
smaller Aesynx and Skrifheim workspace models:
2394+
2395+
- [Fluxheim Modularity Policy](modularity-policy.md): large files and unclear
2396+
crate boundaries are security review risk. New Rust implementation files
2397+
should target 300 lines and stay under 500 lines. Existing large files get a
2398+
staged exception inventory and split plan instead of an immediate blocking
2399+
gate.
2400+
- [Runtime Facts And Policy Proofs](runtime-facts-and-policy-proofs.md):
2401+
Fluxheim should become more aware of its own runtime decisions through typed,
2402+
bounded, redacted facts and small policy-proof objects. This is not a
2403+
database in the request path; it is a safer internal decision shape for logs,
2404+
metrics, traces, admin status, future Wasm hooks, and pentest review.
2405+
23922406
Pre-planning dependency map:
23932407

23942408
| Current Pingora surface | Replacement direction | Notes |
@@ -2421,6 +2435,15 @@ Replacement rules for 1.6:
24212435
`load-balancer`, `stream-proxy`, `php-fpm`, `tls-rustls`, and `tls-openssl`
24222436
map to matching sub-crate features. Avoid hidden default features that pull
24232437
Pingora back into the graph.
2438+
- New substantial code should follow the 300-line target / 500-line hard target
2439+
from the modularity policy. If an existing large legacy file must be touched,
2440+
avoid making it larger unless the same release records an exception update or
2441+
a split step.
2442+
- New security-sensitive decisions should prefer typed decision/proof structs
2443+
over ad hoc booleans and string reasons where practical. The proof model may
2444+
start small, but the direction should be consistent: bounded reason enums,
2445+
explicit policy epochs, redaction classification, and deterministic
2446+
allow/deny/redact/defer outcomes.
24242447
- Every cutover release gets a dependency gate that knows which Pingora crate
24252448
should already be gone. By the final 1.6 release, all official profile
24262449
`cargo tree` runs and container builds must fail if any Pingora crate appears.
@@ -2449,6 +2472,13 @@ Planned `1.6.x` sequence:
24492472
`snapshot.rs`, `acme.rs`, `headers.rs`, `proxy_protocol.rs`,
24502473
`trace_context.rs`, `runtime.rs`, `proxy.rs`, and `admin.rs`, so later
24512474
cutovers are ordered by dependencies rather than file size.
2475+
Add the first report-only modularity gate: list non-generated Rust files over
2476+
500 lines, create a legacy exception inventory with split targets, and fail
2477+
only for new oversized files or legacy files that grow without an exception
2478+
update. Add the initial runtime-fact and policy-proof design inventory:
2479+
fact kinds, decision kinds, bounded reason enums, policy epoch terminology,
2480+
redaction/visibility levels, and the first candidate subsystems to adopt
2481+
proof-shaped decisions.
24522482
- `v1.6.1`: load-balancer independence. Remove `pingora-load-balancing` from
24532483
normal builds. Replace remaining
24542484
Pingora background/listen/shutdown service traits in
@@ -2561,6 +2591,12 @@ Stable exit criteria:
25612591
preserved.
25622592
- The root `fluxheim` crate remains orchestration glue; large runtime domains
25632593
live in focused workspace crates.
2594+
- New or split Rust implementation files stay under the modularity policy's
2595+
500-line hard target unless documented as a temporary exception with a split
2596+
plan. Legacy oversized files have an actively shrinking exception inventory.
2597+
- Security-sensitive decision paths increasingly return typed policy proofs or
2598+
runtime facts that are bounded, redacted, and safe for logs/metrics/traces or
2599+
explicitly marked internal-only.
25642600
- Pingora-removal work does not add Wasm, HTTP/3/QUIC, UDP/GSLB, WAF, or
25652601
VPN/firewall appliance behavior.
25662602

0 commit comments

Comments
 (0)