Skip to content

Ingress & Domains

Forginate uses up to three hostnames, all driven by values — nothing is hardcoded.

HostnameValues keyPurpose
forginate.example.comglobal.domainSPA, API, WebSockets, OAuth callbacks, webhooks
*.apps.forginate.example.comglobal.appsWildcardTenant hosted apps (one subdomain per project)
share.forginate.example.comapiGateway.shareHostPublic customer-facing document review portal

Prerequisites

  • An ingress controller whose class name you put in global.ingressClass.
  • cert-manager with a ClusterIssuer named in global.certIssuer.
  • The wildcard certificate requires a DNS-01 solver (HTTP-01 cannot issue wildcards) — configure your DNS provider's API credentials on the issuer.
  • DNS records pointing at your ingress: A/CNAME for the app domain and share host, and a wildcard record *.apps.forginate.example.com.

The main Ingress

The chart renders one Ingress (named dev-forginate in the template) routing global.domain → the api-gateway Service on port 80 (container port 8080):

yaml
metadata:
  annotations:
    cert-manager.io/cluster-issuer: <global.certIssuer>
spec:
  ingressClassName: <global.ingressClass>
  rules:
    - host: <global.domain>
      http:
        paths:
          - path: /
            pathType: Prefix
            backend: { service: { name: api-gateway, port: { number: 80 } } }
  tls:
    - hosts: [<global.domain>]
      secretName: dev-forginate-tls

Everything rides this one host: the SPA bundle, /api/*, WebSocket upgrades (including /agent/ws for customer deploy agents), /webhooks/github, /webhooks/stripe, and OAuth callbacks.

WebSockets and long-lived streams

The api-gateway serves WebSockets (run event streams, deploy-agent sockets) and the chat-editor path proxies Server-Sent Events. Make sure your ingress controller and any fronting proxy/CDN allow WebSocket upgrade on this host and don't buffer or idle-kill long-lived connections. Deploy agents on customer clusters connect outbound to wss://<global.domain>/agent/ws — no inbound access to their clusters is needed, but your edge must let those sockets stay open.

The internal listener is never exposed

The gateway has a second listener on port 8081 (api-gateway-internal Service, ClusterIP). It serves /internal/* routes authenticated by per-run agent tokens and the shared internal service token. Factory-runner pods reach it at api-gateway-internal.<namespace>.svc.cluster.local:8081.

Never create an Ingress for api-gateway-internal. The chart deliberately doesn't.

Tenant-apps wildcard certificate

The chart renders a cert-manager Certificate named apps-wildcard covering *.<global.appsWildcard> and <global.appsWildcard>, stored in the apps-wildcard-tls secret:

yaml
spec:
  secretName: apps-wildcard-tls
  issuerRef: { name: <global.certIssuer>, kind: ClusterIssuer }
  dnsNames:
    - "*.apps.forginate.example.com"
    - "apps.forginate.example.com"

Environment gate in the template

As shipped, apps-wildcard-cert.yaml only renders when global.environment == "dev" — the template guards on both .Values.global.appsWildcard being set and the environment equaling dev. If you run with environment: prod and want hosted tenant apps, create the equivalent Certificate yourself (or patch the template) — this is a known sharp edge.

Per-project ingresses for hosted apps are created dynamically at deploy time and terminate TLS with apps-wildcard-tls.

The share host

SHARE_HOST / SHARE_PUBLIC_BASE_URL (from apiGateway.shareHost) define a second public hostname used for customer-facing document review links (/s/<token>). When a request arrives with the share Host header, the gateway's host-gate restricts it to only the share surface (/s/*, /share/*, /api/share/*, /api/health) — the share host can never reach the control plane, even if your edge auth differs per host.

Point the share hostname at the same ingress/Service as the main domain (add a second Ingress rule or a copy of the Ingress with the share host). If you don't need the share portal, an empty SHARE_HOST disables the gate (single-host installs).

Cookies and base URLs

  • PUBLIC_BASE_URL = https://<global.domain> — used to build the GitHub OAuth callback (/auth/github/callback), GitHub App callbacks, and webhook URLs. Your GitHub OAuth app must be registered against exactly this origin.
  • COOKIE_DOMAIN = <global.domain>, COOKIE_SECURE=true — session cookies only work over HTTPS on the configured domain. There is no chart knob to disable secure cookies; terminate TLS properly rather than fighting this.

Edge protection (optional)

If you put an authenticating proxy or zero-trust layer in front of the app domain, you must exempt:

  • /webhooks/github — GitHub's webhook IPs (published at https://api.github.com/meta, key hooks) must reach the gateway unauthenticated; signatures are verified by HMAC.
  • /webhooks/stripe — same story for Stripe's webhook IPs, if billing is enabled.
  • /agent/ws — customer deploy agents authenticate with their enrollment key/agent token, not your SSO.
  • The entire share host — it is customer-facing by design.

Forginate — build software with an AI factory.