Reorganize package tree around the three guarded surfaces (CLI / HTTP / MCP) + shared pkg #113
Labels
No labels
burndown-2026-06
sunday-sprint
coherence-core
consult
headless
interactive
P0
P1
P2
P3
P4
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
coilyco-flight-deck/cli-guard#113
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
cli-guard has grown ~40 flat top-level packages that each accreted around one of
three guarded surfaces plus a shared core. The flat layout no longer signals
which package belongs to which surface, and the surface boundaries are now the
real architectural seams. Propose reorganizing the tree around them.
The framing that crystallized this:
agent and an existing binary, audit and validate every argv before
execve.with no first-class CLI (Forgejo). Guard the request, not the subprocess.
chaos through tool servers rather than shells.
config/cache plumbing.
Why now
guard an HTTP call" should land in one subtree, not grep 40 dirs.
(CLI). The seams are now load-bearing enough to make explicit in the package
tree.
pkg, never on eachother. Today nothing stops
egressimportingpassthrough.Proposed bucketing (starting point, not final)
CLI passthrough surface
passthrough,verb,repocfg,allowlist,catalog,gittree,hook,hookcfg,shim,doctor,sudo,shell,dispatch,lockdown,profiles,profile,decisionHTTP surface
egress,specverb,guardfile,specgen,specdrv,respfmt,ghcache,ghidcache,ghratelimit,stscacheMCP surface
mcporterpkg (shared core)
audit,policy,scope,exitcode,config,ttlcache,workdir,skillgencmd/,docs/,examples/,scripts/stay where they are.Open questions
surface/http/egressvs. a flathttp/? Go discourages deepnesting but the surface prefix is the point. Lean shallow:
cli/,http/,mcp/,pkg/.*cachepackages live - underpkg(generic) or under thesurface that drives them (
gh*underhttp,stscacheunder... CLI? both)?Probably
pkg/cachefor the generic TTL machinery, surface dirs for thedomain-specific caches.
decision/profiles/lockdownare cross-surface (they gate both CLI andpotentially HTTP/MCP). May belong in
pkg, not the CLI bucket.forgejo.coilysiren.me/coilyco-flight-deck/cli-guard/<pkg>, so every consumer(ward, coily) re-imports. Needs a single atomic rename commit + a coordinated
consumer bump off the resulting tag. cli-guard is the base of the triple, so
sequencing matters.
surfaces.md/ updatedocs/FEATURES.mdto group theinventory by surface in the same pass.
Scope of work
git mv+ import rewrites,goimports).docs/FEATURES.mdgrouped by surface; add a shortdocs/architecture.mdnaming the surfaces and thepkg-only-downward importrule.
Filed from a design conversation - the bucketing above is a proposal to react
to, not a settled plan.
Evidence from the
speakeasy-wardproving slice + a usage-weighted read on the bucketsLanded the consumer-side "front, don't port" slice in
speakeasy-ward:
a generated client guards
org-deleteby calling a Go endpoint over cli-guard'sdecision+audit+profiles, with zero enforcement reimplemented in thegenerated language. Two things fell out that bear directly on this reorg.
1.
decision/profiles/auditarepkg, not CLI - now proven, not assertedThe open question flagged
decision/profiles/lockdownas maybe-cross-surface.The slice settles it for the first two. An HTTP caller in a different repo
imported exactly
decision,profiles, andauditand nothing else from theCLI bucket. They gate the HTTP front today, in code, with no
passthrough/verbdependency. They belong in
pkg.lockdownis still CLI-flavored (it drives thedeny-list render), so the split is real: the evaluator (
decision/profiles/profile) is shared, the enforcer-of-a-shell-boundary (lockdown) is CLI.2. The endpoint is a surface-agnostic exposure of
pkg, not an HTTP thingThe slice exposed
decision+auditas a tiny HTTP endpoint and let a generatedTS client call it. But nothing about that endpoint is HTTP-specific. All three
fronts can reach the same enforcement plane the same way:
decision/auditin-process, no endpoint needed.So the decision/audit core wants to sit in
pkgand be callable three ways. Theendpoint server is a thin adapter, not a member of the HTTP bucket. If anything
it argues for a
pkg-level enforcement API that each surface adapts.3. The front is chosen by "is there a good CLI to wrap?", not "is there an OpenAPI?"
Bucketing the real top integrations by which surface actually guards them
(most-used first):
gh,aws,brew,kubectl,git,tailscale,gcloud,az,uv,npm,cargo,go,ssh. ~13 of the top set.forgejo(no first-class CLI),trello(no CLI, weak spec). 1, maybe 2.
datadog,honeycomb,new relic, all otel-wrapped) and package-manager registry fetches.mcporter.The seam rule the data exposes: several of these ship an OpenAPI spec and we
still guard the CLI (
gh/GitHub,kubectl/k8s,az/Azure). The front ispicked by CLI maturity, not spec availability. Forgejo is on HTTP because it has
no good CLI, not because it has a spec. That means the HTTP typed-operation
front stays small by construction: most platforms ship a CLI, so HTTP is the
exception for the CLI-less ones.
4. The HTTP bucket is two shapes, and Speakeasy touches only the smaller one
The proposal's HTTP bucket lumps
egresswithspecverb/guardfile/specgen/specdrv/respfmt. By usage those are two different shapes:egress, the CONNECT-proxy concern) - "what hosts maythis send to." Serves the otel stack and registry fetches. The heavier HTTP
shape by usage.
guardfile/specverb/specgen/specdrv+ theOverlay) - "may this caller invoke this operation." Serves ~
forgejo.Speakeasy only ever helps the second shape (typed clients from OpenAPI), which by
this data is one backend. The enforcement value there is the KDL->Overlay
default-deny compile (
overlaygen, ours) plus the endpoint, neither of which isSpeakeasy's. Conclusion for sequencing: Speakeasy is a demo-grade caller
factory, deletable without touching the boundary. Don't let the consumer-bump
step (item 4 of scope) treat it as a dependency to preserve. The thing to keep
stable across the rename is the
pkgenforcement API and the Overlay compiler.Smaller reaction:
gh*cacheplacementThe proposal puts
ghcache/ghidcache/ghratelimitunder HTTP, but GitHub isguarded via the
ghCLI passthrough, not an HTTP front. If those caches backghoperations they belong with CLI. They only belong in HTTP if cli-guard makesdirect GitHub HTTP calls independent of
gh(rate-limit/id resolution). Worthpinning down before the move, since it's the same kind of "front chosen by CLI
presence" call as above.
Net for the bucketing
cli/is the heavyweight by usage (~13 integrations). Size the reorg effortaccordingly.
http/is real but small, and internally splits into egress (broad) andtyped-operation (~forgejo). Consider naming both rather than one
http/.pkg/should holddecision,profiles,profile,audit,policy,scope- the slice consumed the first four cross-repo.pkg-level API surfaced threeways, not an HTTP-bucket package.