Spec: catalog-driven app bootstrapping and capability groups #2
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lab/iac#2
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?
Spec: catalog-driven app bootstrapping and capability groups
Problem Statement
The iac repo has no single entrypoint for "an app exists". App resources are
added ad hoc across
vault/,kanidm/, and separate repos, and a previousattempt left placeholder modules (forgejo/k8s/kanidm/vault) that are
unplannable: the forgejo root fails provider resolution and the modules
reference providers that don't exist. Meanwhile there is no declarative way to
give developers per-app access across Forgejo, Argo CD, Kubernetes, and Vault —
access is hand-granted against the ~90 per-app UI groups.
Solution
Make
apps/catalog.yamlthe single bootstrap entrypoint for an app: one entrydeclares the platform mechanics (namespace, service accounts, Forgejo org and
repos, Argo app-any-namespace resources, Vault roles) and the developer-facing
capability groups for the app. Provisioning runs as the
platform-operatorclass (elevated credentials); app principals hold per-app capability groups,
never cluster-wide power. The model is specified in ADR-0006 (catalog-driven
capability model) and ADR-0007 (per-system permission matrix).
User Stories
namespace, service accounts, Forgejo org/repos, Argo resources, and Vault
roles are all bootstrapped from a single source.
platform mechanics are declared, so that no resource is hand-created ad hoc.
platform-operatorcredentials, so that app principals never receivecluster-wide power.
across Forgejo, Argo CD, Kubernetes, and Vault, so that one membership gives
me the right level of access everywhere.
independent, so that granting more access means more memberships, never
inheritance that over-grants.
contributor, developer, maintainer, live-debugging,
generated-secret-manager, manual-secret-manager, k8s-secret-viewer), so
that the name says what the group does.
viewerto let me view code/issues, pipelines,namespaced resources (not secrets), metrics/logs, and the Argo app.
contributorto let me create branches and pullrequests in Forgejo.
developerto let me merge PRs, run workflows,create namespaced resources (except secrets), and sync the Argo app.
maintainerto let me commit to protectedbranches (Forgejo Admin level).
live-debuggingto let me exec/attach/port-forward into the app's pods and use the Argo CD web terminal.
generated-secret-managerto let me full-CRUDthe app's generated secrets in Vault.
manual-secret-managerto let me full-CRUD theapp's manual secrets in Vault, without seeing generated secrets.
k8s-secret-viewerto let me view Secrets inthe app namespace (all-or-nothing), distinct from Vault secret access.
<app>-rootto be a nested group that is amember of all capability groups, so that granting root grants everything
for the app, transitively.
groups don't grant, so that its tightness is membership discipline, not
capability.
nothing outside the app, so that an app group can never reach cluster
resources.
human, so that service accounts and CI can be granted the same per-app
access (no separate machine tier).
plan, so that the Forgejo org/repo module can actually run.
providers, so that all roots plan cleanly.
each app (via the k8s module), so that apps land in their own namespace.
creation, so that each app gets its org and repos without manual setup.
developer/maintainer can sync/update only their own app.
that capability groups map to namespace-scoped RBAC.
that
viewerincludes observability access.untouched, so that the new model is additive and non-disruptive.
and human manual writes (ADR-0002), so that the capability groups grant
access, not value-writing.
supports
forgejo_team, so that the per-app team/group mapping can beexpressed.
Implementation Decisions
apps/catalog.yamlbecomes the single source of truth,per app: namespace, service accounts, Forgejo org + repos, Argo
app-any-namespace resources, Vault roles, and the list of activated
capability groups. The catalog drives all modules; each system root reads it
and transforms to its module input shape.
maintainer, live-debugging, generated-secret-manager, manual-secret-manager,
k8s-secret-viewer, root. Strictly independent; progress via more memberships.
<app>-rootis a nested group (member of all capability groups; grantsnothing beyond their union; namespace-scoped; no provisioning powers).
platform-operator(elevated, runs the IaC) vs app principals(humans or machines holding capability groups). No machine tier.
read; developer edit non-secret; maintainer edit+write; live-debugging
get pods + create on pods/exec, pods/attach, pods/portforward;
k8s-secret-viewer get/list secrets; root Role
*incl secrets). NoClusterRole — StorageClasses/Gateways/IngressClasses are cluster-operator
resources.
Code/Issues/PRs; developer Write Code/PRs/Actions/Releases; maintainer
Admin; root Owner). Requires
forgejo_team(1.x provider).developer get + sync; maintainer get + sync + update; root project admin).
generated/<app>;manual-secret-manager full CRUD on
manual/<app>; root both classes.svalabs/forgejowithhost/api_token(notbase_url/token), version~> 1.0to getforgejo_team; the module needs its ownrequired_providersso the localname resolves to
svalabs/forgejo(it currently resolves tohashicorp/forgejo).hashicorp/kubernetes ~> 2.33resolves to 2.38.0 andplans cleanly; keep. The module needs its own
required_providersfor thelocal name.
seanlatimer/kanidm ~> 0.1.10resolves to 0.1.10; thedev_groups module needs
required_providersso the local name resolves(currently the module references
kanidmbut doesn't declare it).app_secretsmodule's k8s-auth variables aredropped (contradicts ADR-0004 VSO read path); per-app identity groups +
aliases on both mounts (oidc, oidc-cli) per the one-alias-per-group
constraint, mapping capability groups to Vault policies.
provider create namespaces; the Argo Application lives in the app's own git
repo (app-any-namespace).
Testing Decisions
tofu validate/tofu planperroot is the primary seam (a root that doesn't plan fails the test).
tofu validateagainst arepresentative catalog (e.g. one small app) and asserting the planned
resources match the ADR-0007 matrix (Role rules, Forgejo team levels, Vault
identity groups, Argo roles).
vault/root already plans against real state; thekanidm/vault roots have lock files and state. New tests are validate/plan
assertions, not unit tests (this is IaC, not an application).
tofu initsucceeding per root (catches thehashicorp/svalabs namespace mismatch).
Out of Scope
(separate flux-clusters tickets; this spec scopes the iac-repo side).
ADR-0004).
Further Notes
arg mismatch (
base_url/token→host/api_token) and the module-levelprovider-name mismatch (module must declare
svalabs/forgejo, currentlyresolves to
hashicorp/forgejo).forgejo_teamrequires provider ≥1.x; pinning~> 0.2cannot express theper-app team/group mapping.
groups"), plans/per-app-policy-rollout.md, ADR-0002/0004.