docs(research): draft phase 0 findings

- objective and settled stack (family ADR-002, published crate map)
- load-bearing gitea surface template with walk rules (OQ-FG-01)
- provisional needs-to-stack mapping and draft permission posture
- upstream asks list draft (OQ-07 items per door)
- seeded open questions OQ-FG-01..10, POC candidates, and the
  phase 0 convergence checklist

Verified: dash hygiene check clean
This commit is contained in:
glm-5.3-flash committed 2026-11-07 06:39:44 +00:00
1 parent 95399b46a2
commit e6867eaaed
1 file changed
+391
+391
View File
@@ -0,0 +1,391 @@
---
status: draft
last_updated: 2026-11-07
---
# alkforge: Phase 0 Research Findings
This document captures Phase 0 (Exploration) findings and open design
questions for the `alkforge` assembly. The objective of Phase 0 per
`docs/sdd_process.md` is: *"Capture vision and guiding principles;
research options; validate approaches; converge on a recommended
approach."* It is the input to Phase 1 (Architecture), and its
conclusions feed two family-level open questions: OQ-06 (alkforge 0.1's
exact feature set and Phase 0 shape) and OQ-07 (the alkgit asks list).
This is a **draft**: the sections below record what is already settled
by family decisions and published crates, then set up the work items
that still need real research. The load-bearing-surface enumeration
(§3) is explicitly incomplete and must be driven by a walk of the live
gitea deployment (behaviors only, never its source, see AGENTS.md
convention 13).
## 1. What alkforge is
alkforge is the family's near-term assembly: a minimal private forge
that replaces the gitea deployment, built by composing family crates
rather than re-implementing them (family ADR-002,
`/workspace/@alkdev/alkfamily/docs/architecture/decisions/002-alkforge-gitea-replacement.md`).
The one-sentence scope from ADR-002/roadmap: a web view for the OSS
projects, public clones for them, and SSH-based git transport for
everything else. Minimal first, because the standing exposure
(unpatchable-by-architecture CVE pressure on gitea, recently realized
as an actual compromise) is unacceptable; built out over time toward
alkgit's longer-term distributed-platform goal.
The binding constraint is the **no-deformation rule** (ADR-002 decision
3): alkforge is an assembly on alkgit's core and may not pull, patch,
or fork it to fit assembly needs. Gaps surface as upstream asks (OQ-07,
see §6), never local workarounds. A shortcut inside alkforge that would
deform a family crate is a family-level decision, by ADR.
The standing security posture (ADR-002 decision 5, family layering.md)
is a day-one design requirement, not an afterwards checklist: secrets
via alkvault, no env-var configuration sprawl, minimal wire surface,
fail-closed capability features, bounded resources on every door. The
CVE classes that took gitea down are structural to avoid, and
exploit-finding is getting automated (structure is the defense).
## 2. What is already settled
The heavy design weight does not live in this repo; it lives in the
published family crates. What alkforge adds is composition, identity
wiring, configuration, and the web surface.
- **Git core**: alkgit 0.1.0 (protocol core, `alk/git` ALPN, git
protocol V2, producer/consumer halves) + alkgit-substrate 0.1.0, the
shipped backend composition (refs/cas in alkstore, objects in
alkblobs, LFS per alkgit ADR-022). Objects, refs, pack handling,
receive-pack/upload-pack semantics: done, ledger-tracked. alkforge
never speaks pkt-line and never shells out to `git`.
- **SSH door**: alkssh 0.2.0, published with the `git` feature (git
channel-type mapping onto alkgit), fail-closed feature graph (a
git-only build cannot serve shells, tunnels, or sftp; family
ADR-003 decision 3), alkvault-backed key sources, per-session
resource limits. The auth bridge (verified key fingerprint, then
identity resolution) is shipped. The `alk/ssh` ALPN is pinned
(alkssh ADR-001) but no shipped leg speaks it yet; alkforge is the
planned first consumer. The topology question (edge termination vs
relay leg) is recorded in alkssh's mapping-table research and is a
door-level detail alkforge adopts, not rediscovers.
- **HTTP gate**: alkhttp 0.8.1 (the family's HTTP adapter shape,
fuzzed), with the git door shape recorded in alkgit's
`docs/architecture/doors.md` (alkhttp `git`/`lfs` features bridging
HTTP transport to alkgit's halves). reverse-proxy (produced,
outside the alk\* namespace) stands in front as the HTTP front door,
per ADR-002 decision 4.
- **SSR engine**: alkgen 0.1.0, released 2026-11-07 specifically with
the alkforge SSR scope (alkgen ADR-006): handlebars-rust templates
over serde contexts, strict mode, deterministic render, partials
first-class, in-process library posture (no alk\* deps). alkforge's
SSR path is settled at the engine choice level; the residue
(OQ-GN-09: in-process render-per-request vs pre-rendering, HTML
output unit) is carried in alkgen's phase 0 and resolved when
alkforge's web-view spec lands. Templates are code: versioned
in-repo, strict-mode render errors, golden-output tests (AGENTS.md
convention 9).
- **Persistence**: alkstore 0.1.0 (mem/sqlite/postgres backends behind
one abstraction, the contract suite as the backend-acceptance gate)
holds relational state: accounts, repos, permissions, forge config.
alkblobs 0.2.2 holds large objects incl. git LFS media (alkgit
ADR-022 keying). alkforge writes no bespoke database code.
- **Substrate + identity/ACL**: alkcall 0.10.1 carries the channels
wire, the operation registry, and the identity/ACL model
(`AccessControl::check(peer_identity)`). The visible-surface =
authorized-surface invariant is spec-level (AGENTS.md convention 6).
- **Secrets**: alkvault 0.2.0 (`decrypt` returns `Zeroizing<String>`;
the plaintext-window fix landed via alkssh's consumer review). Host
keys, stored credentials, and any secret material ride the vault;
config stores hold references, never material (AGENTS.md
convention 5).
- **TLS**: alktls 0.1.0 exists; where TLS termination sits (reverse
proxy edge vs alkhttp) is a deployment-shape question (§7, OQ-FG-08).
## 3. The load-bearing gitea surface (work item #1)
OQ-06 requires enumerating what the gitea deployment is *actually*
used for, separating load-bearing surface from inertia. This repo was
initialized before that walk happened, so what follows is the
provisional list implied by family ADR-002's own scope sentence, each
item marked with what must be confirmed against the live deployment:
| Need | Implied serving shape | To verify on the deployment |
|---|---|---|
| Web view for the OSS projects | alkgen SSR pages behind alkhttp | what pages are actually visited/linked (repo landing, file browse, commit log, README render?); how they are linked to from the outside |
| Public clones of OSS projects | git transport with public-read ACL | protocol mix (HTTP smart vs SSH); anonymous vs authenticated share; refspec/branch expectations of downstream users |
| SSH git transport for everything else | alkssh `git` feature over alkgit | who holds keys; write vs read split; whether any non-git SSH usage exists |
| Repo/account model | alkstore rows | org vs user layout; how many repos; who can create/delete |
| LFS or large media | alkblobs + alkhttp `lfs` door | whether LFS is actually in use |
| Issues / PRs / releases / wiki | TBD (not implied by ADR-002 scope) | whether any of it is load-bearing or pure inertia |
Rules for the walk (recorded so the next session does not re-derive
them):
1. Behavior only: enumerate endpoints, features, and workflows from
the outside (what is configured, what is used in logs/configs),
never from gitea source (license posture, AGENTS.md convention 13).
2. Every finding lands in one of three buckets: load-bearing (must
appear in the 0.1 spec), inertia (dropped, with a migration note),
deferred (real need, later version).
3. The output becomes the feature-freeze line for alkforge 0.1 and the
migration plan's source of truth.
## 4. Mapping needs onto the family stack
Provisional composition (draft; refined by §3's findings):
```
reverse-proxy (front door)
│
┌──┴────────────────────────────────────────────┐
│ alkforge (the assembly) │
│ config (alkstore) · vault refs (alkvault) │
│ repo/permission model (alkstore) │
│ identity/ACL wiring (alkcall) │
│ web UI (alkgen SSR pages) │
└──┬───────────┬──────────┬─────────────────────┘
│ │ │
alkhttp alkssh alkstore/alkblobs
(HTTP gate, (SSH door, (persistence; git objects
git/lfs git + LFS media via
features) feature) alkgit-substrate)
│ │
└───── alkgit (git core: produce/consume
halves over `alk/git`)
```
Per-need mapping:
- **Clone/push over SSH**: alkssh `git` feature maps the exec/subsystem
channel onto alkgit's protocol halves; ACL decision from the
fingerprint-resolved identity; fail-closed declines for any other
channel type come from the feature graph alkforge's build enables.
- **Public clones over HTTP**: alkhttp's `git` feature bridging to
alkgit; the "public read" property is an explicitly granted
capability per repo (never a global anonymous default). Details of
the anonymous-peer identity story are OQ-FG-03.
- **Web view**: browser → reverse-proxy → alkhttp ops → alkgen render.
Pages are strictly server-rendered HTML (no client-side framework in
the minimal forge); rendering is deterministic, so caching headers
and render tests both work off golden output.
- **Forge state** (accounts, repos, permissions, config): alkstore
rows; the reactive pattern follows alkstore's established usage in
the family rather than bespoke plumbing.
- **Secrets**: alkvault references in alkstore config; SSH host keys
via alkssh's vault-backed key sources; any outbound credential is
injected at the assembly layer (`OperationContext.capabilities`).
## 5. The repo/permission model (draft posture)
The model ADR-002 names: OSS projects are public read, everything else
is private. The draft posture this phase 0 seeds:
- **Repos** are rows in alkstore with an owner identity and a
visibility field; **grant lists** (explicit capability) sit beside
them. The ACL check at every door is `AccessControl::check` against
the resolved peer identity; "public read" is a capability the door
grants to the anonymous peer class for repos carrying the public flag.
- **Accounts**: a minimal account model (identity, keys, membership)
in alkstore. Key management rides the alkvault/alkssh key-source
model already shipped; the forge UI's account model (registration?
invite-only?) is OQ-FG-04 and should be sized to the actual usage
found in §3 (the family forge is probably invite-only territory).
- **Write access**: pushes resolve fingerprints to identities the same
way alkssh's auth bridge works; the write capability is a per-repo
grant checked at the door before alkgit's receive-pack sees the
session.
- Nothing here introduces a new wire format; permissions are
assembly-side alkstore state read by door-side ACL wiring.
## 6. The upstream asks list (OQ-07 draft)
The no-deformation rule means gaps between what alkforge needs and
what the doors/core expose are filed upstream. The draft asks list,
from what phase 0 already knows (each gets validated and then filed in
the owning repo; nothing here is implemented in alkforge instead):
**To alkgit:**
1. Auth/identity surface for consumers beyond raw protocol operation:
where the consumer (alkforge) hooks per-session authorization
before upload-pack/receive-pack run (the "auth surface for the web
view and the doors" item in OQ-07's note). Possibly already shaped
by alkssh's git mapping; verify and re-scope before filing.
2. Public-read serving posture: what the producer half needs for an
anonymous, read-only peer (rate limits, ref advertisement caps).
3. SSH wiring: confirm the alkssh `git` feature's channel mapping
matches alkforge's needs end to end (it shipped with alkforge as
the named first consumer; gaps found become asks, not workarounds).
**To alkhttp:**
4. The git door's anonymous-request shape (public clone without an
identity), including how it composes with reverse-proxy in front.
**To alkgen:**
5. OQ-GN-09 residue: alkforge's SSR library posture (in-process
render-per-request vs pre-rendered) and whatever the first real
render turns up (0.1.0 exists precisely to be shaken by this).
**To alkcall:**
6. Only if the ACL wiring hits substrate gaps; the visible-surface =
authorized-surface model looks sufficient for the draft posture.
The asks-list discipline from the family ledger: early consumers file
asks; they never work around substrate issues locally.
## 7. Seeded open questions
Numbered OQ-FG-NN for later promotion to `docs/architecture/
open-questions.md` in Phase 1. Order below is rough priority.
### OQ-FG-01: What is the exact load-bearing gitea surface?
Section 3's walk. Blocks the 0.1 feature freeze, the migration plan,
and the web-view spec. Highest priority; everything downstream sizes
from it.
### OQ-FG-02: What is alkforge 0.1's binary/service shape?
alkforge is the assembly; unlike the protocol crates there is no
"library until a consumer asks". Draft hunch: one tokio binary
embedding alkhttp (with alkgen rendering wired behind it) and alkssh
(SSH door), alkstore as the persistence layer, reverse-proxy in front
handling TLS (alktls's role then depends on where TLS terminates, see
OQ-FG-08). Confirm in §3 whether any deployment constraint (systemd
unit, container, port layout) shapes this. This decides `src/lib.rs`
vs `src/main.rs` posture and the config file surface.
### OQ-FG-03: How do anonymous peers resolve to an identity class?
The web view and public clones both receive unauthenticated requests.
The draft posture: the doors map "no valid identity" to a peer
identity class that ACLs can grant read capabilities to explicitly.
Confirm this composes with alkcall's `AccessControl` model and alkhttp/
alkssh door auth, and that rate limiting attaches to the class, not
per-peer.
### OQ-FG-04: Account model for the forge UI?
Minimal: what §3 finds (family forge suggests invite-only, small
count). Registration vs admin-provisioned, session handling for the
browser surface (alkstore-backed sessions? signed tokens?), and where
session keys live (alkvault). Deliberately minimal in 0.1; the
distributed-platform goal lives in alkgit, not here.
### OQ-FG-05: What does the web view render in 0.1?
The page list sizes from OQ-FG-01. Draft hunch: project listing, repo
landing (README + clone lines), file browse/download. Commit browsing
scope (log, tree, diff) is a real 0.1 question: alkgit serves protocol
operations, not web-browse queries; the ask list (§6.2/6.3 territory)
may need an item for cheap ref/object queries for UI purposes, vs
serving them from alkstore metadata. HTML sanitization policy for
README rendering is part of this question.
### OQ-FG-06: Config file and storage layout?
No env vars (AGENTS.md convention 5). Draft hunch: a TOML/JSON config
file pointing at the alkstore backend URI and alkvault references;
paths for the alkstore/alkblobs stores. Config values that are secret
must never live in the file, only alkvault references. Decides whether
config lives in alkstore rows vs a static file per deployment.
### OQ-FG-07: Migration path from the gitea deployment?
Sequence the cutover: git data (alkgit import path exists? gitea's
bare repos are ordinary git repos, so a clone-push migration through
protocol halves is likely), account/key data regeneration into
alkvault/alkstore, and the reverse-proxy endpoint swap. Sized by
OQ-FG-01's findings.
### OQ-FG-08: Where does TLS terminate?
reverse-proxy at the edge (hunch) vs alkhttp carrying alktls. Also
whether any internal leg needs TLS (alk/git and alk/ssh over loopback
or a private network). Confirm with the reverse-proxy deployment's
actual posture.
### OQ-FG-09: What bounded-resource posture does the web surface need?
The doors carry their own limits (alkssh per-session, alkhttp body/
streaming); the assembly still owns page-render and walk-request
limits (per-request wall clock, max rendered size, per-peer concurrency
limits) at alkhttp's op registry level.
### OQ-FG-10: Does anything in the web surface need alkcall ops vs direct wiring?
The assembly has a choice: expose forge operations as registered
alkcall ops (uniform ACL) or wire alkhttp handlers directly to
internal functions. Draft hunch: web-view rendering is direct wiring
with ACL at the HTTP layer, while transport-critical state changes
(repo create/delete, permission grants) go through alkcall ops so the
ACL model covers them uniformly.
## 8. POC candidates
None committed yet. The candidates the seeded OQs imply, in rough
priority (each run per the POC-placement rules in
`docs/sdd_process.md`; findings merge back here):
1. **Deployment walk** (not a POC; pure research): the §3 gitea
surface enumeration. Blocks OQ-FG-01 and most sizing decisions.
2. **End-to-end git round trip through the assembled doors** (likely
thin over alkssh's and alkgit's existing test coverage; only run
what the crates do not already prove): clone + push over alkssh git
mapping and over alkhttp's git feature against an
alkgit-substrate backend, with an ACL grant/decline matrix. Validates
§4's composition and the asks list items 1/2/4.
3. **SSR first-consumer spike**: one repo-landing page through alkgen
0.1.0 behind an alkhttp op, with strict-mode failure and
determinism tests. Validates item 5 (OQ-GN-09 residue) from
alkforge's side and turns up the first alkgen asks.
## 9. Convergence checklist (what Phase 0 must produce)
- [ ] Load-bearing gitea surface enumerated (OQ-FG-01; §3 template
filled with real findings)
- [ ] 0.1 feature freeze list agreed from those findings
- [ ] Stack mapping validated (§4) against the real surface; updated
with door gaps
- [ ] Upstream asks list (OQ-07) finalized and filed in the owning
repos
- [ ] Repo/permission model (§5) and identity wiring settled enough
for the Phase 1 spec
- [ ] Config/secrets layout (OQ-FG-06) dispositioned
- [ ] Migration path (OQ-FG-07) sketched
- [ ] OQ-FG seeds dispositioned or carried to Phase 1's
open-questions.md
- [ ] Family OQ-06/OQ-07 updates written back to
`/workspace/@alkdev/alkfamily/docs/architecture/open-questions.md`
## 10. References
- Family ADR-002:
`/workspace/@alkdev/alkfamily/docs/architecture/decisions/002-alkforge-gitea-replacement.md`
(the structural decision: scope, no-deformation, security posture)
- Family docs: `ledger.md` (wire-stable contracts incl. `alk/git`,
`alk/ssh` status), `layering.md` (where alkforge sits),
`family-map.md` (crate versions/roles),
`architecture/open-questions.md` (OQ-06/OQ-07),
`tasks/architecture/oq-06-alkforge-phase-0.md` (the tracker task)
- alkgit: AGENTS.md, `docs/architecture/` (esp. `doors.md` for the
alkhttp/alkssh door shapes, `backend.md`/`substrate.md` for the
alkgit-substrate composition, `lfs.md`)
- alkssh: AGENTS.md, `docs/research/vision.md` /
`auth-bridge.md` / `mapping-table.md` (the git feature and auth
surface alkforge consumes)
- alkhttp: AGENTS.md, `docs/architecture/` (`http-adapters.md`,
`http-server.md` for the gate shape alkforge mounts)
- alkgen: `docs/research/phase-0.md` (the SSR branch, OQ-GN-09) and
ADR-006 (SSR engine decision)
- alkstore: docs + contract suite (backend acceptance gate)
- alkcall: docs/architecture (identity/ACL model: `AccessControl`,
`Visibility`)
- reverse-proxy: `/workspace/@alkdev/reverse-proxy` (front door
precedent)
- The gitea deployment itself: live inspection target for §3
(behavior only)