What a tenant can and cannot do on this platform. Each boundary is a guarantee, and each guarantee names the mechanism that enforces it. Enforcement is structural — Forgejo permissions, branch protection, a rootless CI pool, image provenance, and network policy — never policy on paper.
The boundaries
The table lists each guarantee with the mechanism that enforces it.
| Guarantee | Mechanism |
|---|---|
| CI code cannot reach the node, other tenants, or the cluster API | A non-privileged, isolated runner pool: no Docker daemon, automountServiceAccountToken: false, an egress-allowlist NetworkPolicy |
| CI code cannot reach cloud metadata or the private network | Egress denies RFC1918 (10/8, 172.16/12, 192.168/16) and 169.254.169.254/32 |
| A workflow cannot push another org's images | Forgejo delegates package write only through an org team; REGISTRY_TOKEN is a per-org write:package-only credential |
| A workflow's token works on exactly one repo | github.token is an ephemeral per-run token, scoped to that repo |
| An agent cannot write outside its assigned repo | A per-run SSH deploy key covers one repo; the platform deletes it when the run exits |
| Tenants cannot enumerate each other | Orgs and repos are private by default; org membership is hidden |
The CI boundary
All CI runs on one pool. Runner jobs are ephemeral, scale from zero, and claim runs-on: isolated. The pod shape is the security boundary:
- No Docker daemon, anywhere. Steps run in host mode inside the toolchain container. Image builds go through a rootless BuildKit sidecar (
runAsUser: 1000,privileged: false) over a UNIX socket. No privileged pool exists. Aruns-on: dockerjob is never claimed by anything; it waits forever. - No cluster credentials.
automountServiceAccountToken: falsekeeps the kube API token away from tenant code. - An egress-only allowlist NetworkPolicy. It permits DNS, the Forgejo namespace (clone and registry pull), traefik (HTTPS registry push), and the MCP endpoints
/ci/previewand/ci/sync-env. Those endpoints authorize each call by a check of the caller's Actions token for push rights, and they fail closed. Public internet egress is open, except RFC1918 ranges and the cloud-metadata address169.254.169.254/32. - A bounded lifetime. Each runner pod registers ephemerally, serves one job, and stops at a hard deadline.
The toolchain inside the pool is covered in CI and Builds.
The workflow surface
You author any workflow you like in your app repos. A small set of controls keeps that open surface safe:
guard-ppt. A required status check (guard-ppt / no-ppt*) fails any PR whose workflows usepull_request_target— the trigger that hands base-repo secrets to untrusted PR code. The check is required on open-platform/mitosis and the app template today. Making it required on every app repo is an open follow-up.- Fork PRs run without secrets. A fork's
pull_requestrun gets an empty secrets map. An untrusted fork author needs manual approval before anything runs. The base branch's workflow definition runs — not the fork's. - The one host-executing workflow is actor-gated. The coldstart gate runs PR code directly on a host, so it admits only an explicit allowlist of trusted actors. The gate keys on the actor, not the fork, because an AGit PR places its head in the base repo.
- System repos lock their workflow files. A branch-protection rule named
**carriesprotected_file_patterns: .forgejo/workflows/**;.gitea/workflows/**;.github/workflows/**. A non-admin pusher — including every agent deploy key — cannot add or alter CI workflows on any branch. New app repos skip this rule on purpose, so you keep workflow authorship. - The merge gate. The required
check / check*status gates every merge to main.
Credential scope
github.tokenin a workflow is Forgejo's ephemeral per-run token. It answers 200 on its own repo and 404 on every other repo, same-org included.REGISTRY_TOKENbelongs toregistry-bot-<org>: a restricted user in a packages-only team (registry-writers,units_map: {"repo.packages": "write"}) with awrite:package-only token. This delegation model is also why apps must be org-owned — Forgejo delegates package write only via an org team.- Agents push with a per-run read-write deploy key. The key covers exactly one repo, and the platform deletes it when the run exits. The key is non-admin, so branch protection binds on it.
- Platform bots are restricted users with narrow write scopes. No admin credential sits within tenant reach.
- Self-serve secrets are write-only. A read of a single value returns HTTP 405. The sync-env pipeline drops reserved names (
REGISTRY_TOKEN,FORGEJO/GITEA/GITHUB_TOKEN,PLAT_*,APP_ENV) before it seals anything into your app. See Secrets and Config.
The app namespace
Every app namespace is fenced independently of CI. pod-security.kubernetes.io/enforce: restricted applies to every pod. Kyverno generates a ResourceQuota, a LimitRange, and default-deny NetworkPolicies, keyed off the plat.sh/app label with self-healing sync. Twelve ClusterPolicies enforce — among them registry restriction, image-provenance verification before deploy, and a ban on admin ClusterRoleBindings. The one exception is the forwardauth-on-app-ingress policy, which is not yet enforced. The full roster is in Platform Internals.
Privacy defaults
New repos and orgs are private by default (DEFAULT_PRIVATE: private, DEFAULT_ORG_VISIBILITY: private). DEFAULT_ORG_MEMBER_VISIBLE: false closes org and org-package enumeration. The bulk users page is disabled. Anonymous browsing shows only the public seed repos. Where registration is open, it uses a sovereign image CAPTCHA — no third-party service — and internal accounts only. A new member signs in and sees exactly their own orgs, nothing else. Roles and teams are covered in Users Teams and Access.
For everyone
Experience
- Build Your First App
- Building with Issues
- Dependency Trees
- Routines
- Orgs and Planning
- Preview Environments
- Connect Your Agent
Operate
Under the hood
Grow
This wiki ships inside open-platform/mitosis (wiki/) — edit it there, not here. Grown by the platform it describes.