Skip to content

App Access and Sharing

Forge access has three separate controls. Keep them separate during review and incident response.

Control Where it is set What it changes
Route exposure public_edge.exposure in mithran.yaml The reviewed app-route exposure value. public serves anonymously; all other values keep Forge auth required.
Protected sharing access.yaml, map access plan, and map access apply Which signed-in users, email domains, or account: principals should be included on protected route entries.
Published pointer map publish Which reviewed version the clean public URL serves.

map access apply does not edit mithran.yaml, build a new version, publish a version, or retire a route. It records the reviewed protected-sharing policy for the app. The next route push or route reconcile carries the current allowed_domains and share values onto served route entries.

Review Boundary

Review question Source of truth Values that matter Customer-visible effect
Can an anonymous user reach the app route? public_edge.exposure in mithran.yaml public serves anonymously. protected, none, public-redirect, and unset require Forge auth. Requires a reviewed manifest commit, deploy, and publish when the clean public URL should move.
Which signed-in users can reach an auth-required route? access.yaml, map access plan, map access apply allowed_domains and share; access-policy exposure accepts only protected or public. Takes effect on the served route after the next route push or route reconcile carries the policy.
Which reviewed version does the clean public URL serve? map publish Deployment ref selected by the operator. Moves the clean public URL to that reviewed version.

Use mithran.yaml for route exposure. Use access.yaml for protected sharing. Do not use access.yaml to make a manifest-protected route anonymous.

Choose the Right Change

Goal Do this Verify with
Keep an app Forge-authenticated Set public_edge.exposure to protected, none, public-redirect, or omit it, then deploy and publish when needed. Anonymous request is denied before reaching the app runtime.
Serve an app anonymously Set public_edge.exposure: public, then deploy and publish when needed. Anonymous request reaches the app route.
Restrict a protected app to a domain or principals Review access.yaml, run map access plan, then map access apply; update or publish the route that should carry the policy. Allowed signed-in user succeeds; denied signed-in user fails.
Move the clean public URL to a reviewed version Run map publish for a version that reached Succeeded or Promoted. map versions <app> shows published on the intended deployment ref.
Stop new source fetches Remove the repo from the Forge GitHub App grant. New deploy source fetches fail or stop; existing served routes are separate.

Request Path

App access is enforced before traffic reaches the app process:

  1. The client opens a Forge app hostname.
  2. The Forge edge resolves the routing record for that hostname.
  3. The edge checks the route exposure carried by that route entry.
  4. For auth-required routes, the edge checks the signed-in user against the protected sharing policy attached to the route.
  5. Only allowed requests are forwarded to the app runtime.

An access denial on a protected route is decided at the edge. The app process should not implement a second copy of the same route-sharing policy just to compensate for a missing access.yaml update.

The served route's auth_required value comes from the deployed manifest exposure. Forge projects the recorded allowed_domains and share policy onto the route for per-user checks when authentication is required.

Exposure

Set route exposure in mithran.yaml:

mithran.yaml
public_edge:
  type: platform
  exposure: protected
Value Route behavior
protected Keeps auth_required: true. Forge auth is required before forwarding.
none Keeps auth_required: true.
public-redirect Keeps auth_required: true. This is a manifest exposure value, not an access-policy value.
public Sets auth_required: false. Anonymous requests can reach the app route.
unset Keeps auth_required: true.

public_edge.type must be platform when public_edge is present. Provider-specific WAF, Cloud Armor, or ingress expressions do not belong in mithran.yaml.

Exposure is reviewed with the app source because it lives in the deploy manifest. Changing exposure requires a reviewed manifest commit, deploy, and publish when the clean public URL should move to that version.

Hostnames

The served app ref determines the hostname label. For self-service apps, keep the served app ref aligned with metadata.app_id:

mithran.yaml
metadata:
  app_id: inventory-portal
identity:
  project_ref: app:inventory-portal

When the app is served as app:inventory-portal, the production clean public URL is:

https://inventory-portal.apps.mithran.cloud/

Changing the served app ref changes the hostname. Treat app-ref or app-ID changes as route changes: deploy the reviewed manifest, publish the reviewed version if the clean public URL should move, then verify the served hostname.

Use map versions <app> before and after publish-sensitive access work. It shows internal versions, app-environment aliases, and the published pointer. The clean public URL moves only when map publish pins it to a reviewed version.

Protected Sharing Policy

For protected apps, keep the sharing policy in access.yaml:

access.yaml
app_ref: app:inventory-portal
tenant_ref: tenant:acme
account_ref: account:acme-main
exposure: protected
allowed_domains:
  - acme.com
share:
  - admin@example.com
  - account:acme-main
Field Required Notes
app_ref yes, unless --app-ref is passed App ref, such as app:inventory-portal.
tenant_ref no Tenant ref to write with the policy. CLI flag --tenant-ref overrides the file.
account_ref no Account ref to write with the policy. CLI flag --account-ref overrides the file.
exposure no Access-policy exposure. Defaults to protected in the CLI. Must be public or protected.
allowed_domains[] no Email domains admitted by the protected sharing policy.
share[] no Explicit principals, such as email addresses or account: refs.

Unknown fields are rejected. A typo such as allowed_domain fails instead of silently applying a different policy.

The access-policy exposure value is not the same as manifest exposure. Manifest exposure is reviewed from mithran.yaml. Access-policy exposure is recorded by map access apply and accepts only public or protected; setting access-policy exposure to public does not make a route anonymous. To serve anonymously, deploy a reviewed manifest with public_edge.exposure: public, and publish it when the clean public URL should move to that version.

Matching Rules

Use allowed_domains for domain-level sharing:

allowed_domains:
  - acme.com

A signed-in user whose verified email domain is acme.com is admitted by that policy. Do not include @ in allowed_domains.

Use share for exact principals:

share:
  - admin@example.com
  - account:acme-main

Email entries admit the named signed-in user. account: entries admit the named account principal.

For a protected route with no allowed_domains and no share entries, the route remains auth-required and admits signed-in users by default. Add at least one domain or share principal when the app should be limited to a customer domain or named principals.

When onboarding includes an account ref, Forge can seed that account: principal into the app's initial sharing policy so the owner can reach the first protected deploy. A subsequent map access apply replaces exposure, allowed_domains, and share with the reviewed policy.

Plan and Apply

Plan locally before writing anything:

map access plan --repo-dir .

Text output is a summary:

would apply access for app:inventory-portal:
  exposure: protected
  allowed_domains: acme.com
  share: admin@example.com, account:acme-main

Use JSON when reviewing the exact request body:

map --json access plan --repo-dir .

map access plan is local. It reads the file, applies CLI overrides, validates the access-policy exposure, and prints the request body that apply would send. It does not call the control plane and it does not prove that a served route already carries the policy.

Apply the reviewed policy:

map access apply --repo-dir .

The control-plane response includes the resolved values and the effective boundary:

{
  "status": "applied",
  "app_ref": "app:inventory-portal",
  "exposure": "protected",
  "allowed_domains": ["acme.com"],
  "share": ["admin@example.com", "account:acme-main"],
  "effective": "next-deploy-route-push"
}

CLI flags override the file:

map access apply \
  --file ./access.production.yaml \
  --app-ref app:inventory-portal \
  --tenant-ref tenant:acme \
  --account-ref account:acme-main \
  --exposure protected

Applying the policy replaces the app's recorded exposure, allowed_domains, and share values. It does not change the deployed manifest exposure. A served protected route receives the reviewed sharing policy on route push or reconcile.

When a Change Takes Effect

Change Effective when What to verify
map access plan Immediately, locally. The resolved policy matches the review intent.
map access apply Forge records the new policy and returns effective: next-deploy-route-push. Apply response and saved access.yaml match the reviewed policy.
Served protected sharing The served route is pushed or reconciled after apply. Allowed and denied requests against the hostname users open.
Manifest exposure A reviewed manifest commit is deployed and the relevant route is updated or published. map versions <app>, deploy status, and anonymous/signed-in HTTP results.

Apply a Protected Access Change

Use this sequence for customer-visible protected access changes:

  1. Record the current route state with map versions <app>.
  2. Confirm the version serving the hostname has non-public exposure.
  3. Edit and review access.yaml.
  4. Run map access plan --repo-dir ..
  5. Run map access apply --repo-dir ..
  6. Update or publish the route that should carry the policy.
  7. Verify one allowed signed-in user or domain.
  8. Verify one denied signed-in user or anonymous request.
  9. Keep access.yaml, plan output, apply output, map versions <app>, and allowed/denied results with the release or change record.

If the hostname does not reflect the reviewed sharing policy after apply, inspect which routing record users are reaching and move that route again. The policy write is immediate, but a served route entry receives the policy on route push or reconcile.

Change App Route Exposure

Route exposure is a manifest change.

  1. Set public_edge.exposure to protected, none, public-redirect, public, or omit the exposure.
  2. Review access.yaml if the app should be restricted to specific users or domains.
  3. Run map access plan and map access apply when protected sharing is involved.
  4. Deploy the reviewed commit.
  5. Publish the reviewed version when the clean public URL should move.
  6. Verify the route behavior for the selected exposure: anonymous success for public, or anonymous denial plus allowed signed-in access for protected route shapes.

Revoking Access

Choose the control that matches the access you need to revoke.

Goal Customer action
Stop Forge from reading a repo Remove the repo from the Forge GitHub App grant, or uninstall the App from the GitHub account or organization.
Stop custom-CI deploy requests Disable or remove .github/workflows/map-deploy.yml in the repo.
Stop the clean public URL from moving to new deploys Do not publish a new version; map publish is the pointer movement.
Change who can reach a protected app Update reviewed access.yaml, run map access plan, run map access apply, and move the served route that should carry the policy.

Removing a GitHub App grant stops new source fetches. It does not prove an existing served route has stopped serving. Disabling custom CI stops that workflow from requesting deploys; it does not remove built-in webhooks or a served route. Changing protected sharing controls who can reach a protected route; it does not retire the route entry.

Route retirement is outside these customer-owned access controls. Use the route-retirement packet in Support and escalation when a served route itself must stop serving.

Verification Checklist

For protected apps:

  • deployed mithran.yaml has non-public exposure or no public_edge.exposure;
  • access.yaml contains the intended allowed_domains and share values;
  • map access plan output matches the reviewed policy;
  • map access apply succeeds and returns effective: next-deploy-route-push;
  • the route users reach has been updated or reconciled after apply;
  • an allowed user or domain can reach the app;
  • a denied user or anonymous request is denied before reaching the app runtime.

Troubleshooting

Symptom Check
map access plan fails Check access.yaml for unknown fields, missing app_ref, or an unsupported exposure value.
Apply succeeds but access does not change Confirm the served route was pushed or reconciled after apply, and confirm users are opening the route you changed.
Protected app denies an expected user Confirm the user is signed in and the reviewed policy includes the user's domain, email, or account: principal.
Manifest review rejects exposure Confirm public_edge.exposure is protected, none, public-redirect, or public.
unknown_hostname Confirm the clean public URL has been published, route status is ready, and the hostname matches the app ref shown by map versions <app>. See route propagation.