# rapp-bounded-executor intake (owner answer to NK-WP-0056-T02) Status: owner answer from `railiance-apps`, 2026-10-31. Proposal only. Creating the repository, its Forgejo remote and any live deploy remain founder decisions. Request: `net-kingdom/docs/proposals/2026-10-31-nk56-t02-owner-requests.md` (section "railiance-apps"), hub message `41dbcc41`. Founder decision hub `6d1adce9`: NetKingdom writes a bounded executor runtime (standard `net-kingdom/canon/standards/bounded-execution-admission_v0.1.md`); a new `rapp-*` package deploys it in namespace `sso` with ServiceAccount `nk-bind-executor`. ## Short answer 1. **`railiance-apps` does not create or own the repository.** Under ADR-0015 and the Railiance family contract, a `rapp-*` is its own repository. This repository (S5) owns shared app-release lessons and the apps it still hosts (Path C); it is not the factory for rapp repos. 2. **Who creates it:** the founder, by admitting the creation. The tool is `repo-manager` (`rmgr rapp wrap`), run from a workstation checkout with `--ownership-repo net-kingdom`. `wrap` writes local files only. Creating the Forgejo remote (`coulomb/rapp-bounded-executor`) is a separate founder-run step. Every existing NetKingdom rapp was bootstrapped this way (for example `rapp-user-engine`, first commit 2026-08-18, "bootstrap managed package from NetKingdom apply home"). NetKingdom's agent may run `rmgr rapp wrap` locally once the founder says the repo should exist. 3. **The image is not built by the rapp.** Correction to the request wording ("rapp-bounded-executor builds the image"). The fleet rule is that the **app repo** builds and publishes the image digest (`.forgejo/workflows/image.yaml`), and the rapp **pins** that digest. Here the app repo is `net-kingdom`, unless the executor gets its own source repository (see founder item F2). 4. **Deploy path:** `kubectl` server-side apply of digest-pinned manifests through the rapp's Makefile (`render`, `server-dry-run`, `deploy`, `verify-live`, `rollback`). There is no ArgoCD or other GitOps controller in this estate. Helm is optional; the default for a new first-party rapp is `manifest-managed-platform-service`. Live apply is founder-run. ## Conventions that apply (with sources) | Topic | Convention | Source | | --- | --- | --- | | What a rapp is | One Railiance-managed workload; packaging, not ownership | `railiance-master/docs/repository-axes.md`, ADR-0015 | | Declaration | `declarations/rapp.yaml`, validated against the schema; there is no `register-rapp` command | `railiance-master/schemas/rapp.schema.json`, `railiance-master/docs/adr/ADR-0007-rapp-declaration-contract.md` | | Repo baseline | `.repo-classification.yaml`, README, INTENT, SCOPE, AGENTS, CLAUDE, LICENSE, Makefile, `declarations/rapp.yaml`, `workplans/`, `workplans/archived/` | `railiance-master/docs/repo-family-bootstrap-contract.md`; `repo-manager/docs/RailianceAppDeploymentGuide.md` section 6 | | Greenfield path | `rmgr rapp wrap` → human review → CI digest → `rmgr rapp pin-image` → `rmgr rapp place` → operator deploy | Deployment Guide section 0 | | Ownership split | Code, tests, Containerfile, image build: app repo. Manifests, ServiceAccount, NetworkPolicy, Makefile, smoke, digest pin: rapp. Secret values: OpenBao via platform. | Deployment Guide section 5 | | Image | Forge registry `forgejo.coulomb.social/coulomb/@sha256:...`; never a tag; check pull with `railiance-apps/tools/check-oci-image.sh` | Deployment Guide sections 0, 9.4; `docs/s5-app-onboarding-checklist.md` | | Reef binding | `bound_reefs: []` at `declared`; add `reef-railiance` binding in `reef-railiance/bindings/rapps.yaml` only with an explicit substrate decision | Deployment Guide 10.2; `reef-railiance/bindings/rapps.yaml` notes | | Readiness | `draft` → `declared` → `deployed`/`verified` only with evidence | Deployment Guide section 6 table | | Exposure | Omit (private). The executor has no Ingress. | Deployment Guide section 4 | | Closest model | `rapp-user-engine` (manifest-managed, digest pin, `make verify-live`, `make rollback DIGEST=`) | `~/rapp-user-engine` | ## Proposed declaration values | Field | Proposed value | Why | | --- | --- | --- | | `rapp_id` / `repo` | `rapp-bounded-executor` | working name from the request | | `workload_identity.name` | `bounded-executor` (or `nk-bind-executor`) | must not carry `rapp-` | | `ownership_repo` | `net-kingdom` | executor runtime is written there | | `package_type` | `manifest-managed-platform-service` | no existing chart | | `primary_rail` | `rail-kubernetes` | Job semantics; no scale-to-zero need | | `bound_reefs` | `[]` until placement is admitted | | | `data_classification` | `confidential` (consider `restricted`) | it handles directory credentials | | `criticality` | `high` | it writes identity credentials | | `runtime_dependencies` | `kubernetes-api`, `forgejo-oci-registry`, `openbao`, `approval-engine`, `lldap` | from the T02 request | | `secret_references` | the OpenBao paths the secrets-engine answer grants (paths only) | | | exposure | omitted | | ## Workload shape: Job, not Deployment The standard says one admission is consumed once, before the first step. A `Job` per admission (`backoffLimit: 0`, `activeDeadlineSeconds` equal to or shorter than the admission window, `ttlSecondsAfterFinished`) matches that. A long-running Deployment would hold OpenBao and directory reach with no admission in hand. Proposed: the rapp ships a Job template and a founder-run `make run ADMISSION_ID=` that renders the Job with the pinned digest and the admission id and applies it. No CronJob. ## Namespace `sso` implications - The `sso` Namespace object, and its namespace-wide NetworkPolicy (`net-kingdom/sso-mfa/k8s/network-policies/netpol-sso.yaml`), currently live in `net-kingdom`'s migration apply home. The rapp must **not** redeclare the Namespace. It applies only its own objects in `sso`. - The rapp ships its own pod-scoped NetworkPolicy: egress to the OpenBao service, LLDAP, approval-engine and DNS only; no ingress. - `sso` already hosts KeyCape, LLDAP and Authelia. An executor pod there shares their namespace. It needs its own Pod Security settings (non-root, read-only root filesystem, no privilege escalation, dropped capabilities) and `automountServiceAccountToken` only if the Kubernetes Role below needs it. - A separate namespace would isolate it more. The founder chose `sso`; record that choice in the rapp's runtime contract. ## ServiceAccount and RBAC ownership | Object | Owner | Note | | --- | --- | --- | | `ServiceAccount nk-bind-executor` (in `sso`) | rapp | per the ownership split | | `Role` / `RoleBinding` in `sso` (the Kubernetes part of the ceiling) | rapp applies it; **content is set by `net-kingdom`** | the ceiling is NetKingdom canon (BEA standard). The rapp must not widen it. Default: no Kubernetes permissions at all unless a pinned step needs them. | | OpenBao Kubernetes-auth role bound to `sso/nk-bind-executor` (15-minute TTL) and its policy | `railiance-platform` / `secrets-engine` | answer pending from secrets-engine | | LLDAP directory account for the executor | NetKingdom / secrets-engine lane | pending | | Approval consume scope for `service:nk-bind-executor` | approval-engine | pending | | PDP gate (later) | access-engine | pending | ## Proposed skeleton (not created) ```text rapp-bounded-executor/ .repo-classification.yaml README.md INTENT.md SCOPE.md AGENTS.md CLAUDE.md LICENSE Makefile # check, render, server-dry-run, run ADMISSION_ID=, verify-live, rollback DIGEST= declarations/rapp.yaml manifests/ serviceaccount.yaml # nk-bind-executor role.yaml # ceiling from net-kingdom; empty by default networkpolicy.yaml # pod-scoped egress allowlist job.template.yaml # digest placeholder, admission id parameter docs/runtime-contract.md # identity table, namespace sso decision, smoke, rollback docs/platform-handoffs.md # OpenBao role, LLDAP account, approval scope tools/render.py tools/verify_live.sh tests/ # render tests, no cluster workplans/ workplans/archived/ ``` And in `net-kingdom` (app repo): `Containerfile` for the executor and `.forgejo/workflows/image.yaml` (fleet workflow) that publishes `forgejo.coulomb.social/coulomb/bounded-executor@sha256:...`. ## Checklist 1. [ ] Founder: decide the repo should exist (F1) and where the executor source lives (F2). 2. [ ] Founder or NetKingdom agent: `rmgr rapp wrap --path ../rapp-bounded-executor --app bounded-executor --ownership-repo net-kingdom` (local files only). 3. [ ] Founder: create the Forgejo remote `coulomb/rapp-bounded-executor` and push. 4. [ ] NetKingdom: Containerfile and fleet `image.yaml` in the app repo; CI prints a digest. 5. [ ] `rmgr rapp pin-image --digest sha256:...`; `rmgr rapp validate --family-root ..`. 6. [ ] Rapp: ServiceAccount, Role (ceiling from NetKingdom), NetworkPolicy, Job template, Makefile, runtime contract. 7. [ ] Platform owners: OpenBao role and policy, LLDAP account, approval consume scope. 8. [ ] `make server-dry-run` against the cluster (read-only); `check-oci-image.sh` pull check. 9. [ ] Founder: `rmgr rapp place --reef reef-railiance` and a reef binding when the pilot is admitted. 10. [ ] Founder-run first `make run ADMISSION_ID=...` for the T06 pilot; evidence into the rapp. ## Items for the founder - **F1** Admit creation of `rapp-bounded-executor` (local wrap plus Forgejo remote). - **F2** Executor source home: inside `net-kingdom` (current decision) or a separate app repo. If it stays in `net-kingdom`, that repo gains a Containerfile and an image workflow. That is the fleet pattern, but it is new for a canon repo. - **F3** Job (recommended) or Deployment. - **F4** Confirm `sso` over a dedicated namespace, accepting co-tenancy with KeyCape, LLDAP and Authelia.