Skip to content

[FEATURE] Install Bootstrap #3175

Open

Description

Tracks the Boostrap Interface from Extensible Install Roadmap

Goal

Defines what the in-cluster bootstrap creates, so operators who disable it know exactly what to provide, and Helm and provider paths can drive it.

Owner: kustomize release workstream (#3169). Consumers: Helm workstream (#3170), GKE workstream (ai-on-gke/substrate-gke#70).

Working Specification:

Object Namespace Type Keys Content Semantics
actor-id-jwt-pool ate-system Opaque pool, jwks pool: JSON JWT-authority pool, one active key, ID = key thumbprint; ES256 (ECDSA P-256) default, RS256 (4096-bit RSA) optional. jwks: public JWK Set (JSON) Create if absent; jwks recomputed from pool and updated when stale
actor-id-ca-pool ate-system kubernetes.io/tls pool, tls.crt, tls.key JSON CA pool; PEM root chain; PEM key; ECDSA P-256 (since #3251), 365-day root Create if absent
actor-id-ca-certs ate-system Opaque ca.crt PEM root copied from actor-id-ca-pool Create if absent
egress-mitm-ca-pool ate-system kubernetes.io/tls pool, tls.crt, tls.key JSON CA pool; ECDSA P-256 Create if absent
service-dns-ca-pool podcertificate-controller-system kubernetes.io/tls pool, tls.crt, tls.key JSON CA pool; ED25519 Create if absent
pod-identity-ca-pool podcertificate-controller-system kubernetes.io/tls pool, tls.crt, tls.key JSON CA pool; ED25519 Create if absent
postgres-ca-pool podcertificate-controller-system kubernetes.io/tls pool, tls.crt, tls.key JSON CA pool; ED25519 Create if absent
k8s-credential-provider-namespace-policy ate-system ConfigMap namespace-policy.yaml policies: [] Create if absent
  • Not created by bootstrap:
  • the settings objects in [FEATURE] Establish cluster-side install setting records #3172, which ship as manifests;
  • bundled Postgres identities, which Postgres creates itself ([FEATURE] Extensible DB Identity Setup #3184).
  • Image and command. The ate-setup image, running ate-setup bootstrap.
  • Arguments.
  • --database-mode=bundled|external, rendered from the same setting that selects bundled Postgres (Proposed name).
  • --namespace; default ate-system.
  • Guards. If ate-api-server-secret-envvars exists with a non-bundled connection string and the database mode is bundled, or if the separate owner login would be dropped, the Job exits non-zero before the control plane is applied. Exit codes:
    • 0: success;
    • 1: unexpected error;
    • 2: guard refused the change.
  • RBAC. Get and create on Secrets and ConfigMaps in both namespaces, update on actor-id-jwt-pool only, get on ate-api-server-secret-envvars. No pods/exec.
  • Ordering. release/bootstrap/ includes the ate-system and podcertificate-controller-system namespaces. Under Helm, the RBAC and Job run as pre-install and pre-upgrade hooks with ordered weights ([FEATURE] Run the install bootstrap as Helm hooks #3685). The namespaces must not be hooks with before-hook-creation: that policy deletes both namespaces, including the key pools, on every upgrade (see the 2026-11-05 comment). Open: create them from a create-if-missing Job at an earlier weight, or ship them as helm.sh/resource-policy: keep objects in a chart installed before the bootstrap.
  • Disabled mode. With bootstrap.enabled=false (Helm) or the bootstrap base omitted (kustomize), operators create every object in the first table themselves, with the same names, types, keys, and formats. The JSON pool formats are documented with an example generator command.

Consumers of these objects

Consumer Objects
podcertificate-controller pool from the three signer pools
ate-api-server pool from actor-id-jwt-pool and actor-id-ca-pool
ate-idp-server pool from actor-id-jwt-pool (may change to the public JWKS, #3562)
atenet-egress pool from egress-mitm-ca-pool; actor-id-ca-certs
k8s-credential-provider The policy ConfigMap

Open points

Enforcement

  • Running bootstrap twice changes nothing.
  • A test cluster with pre-created objects starts with bootstrap disabled.
  • The guard test exits 2 on a forbidden switch.

Implementation: #3676. Related: #3273.

IMPORTANT: Working spec is a point-in-time analysis. Review latest changes.

Parent Issue: #3160

Activity

  1. tryambak2019 commented on Oct 25, 2026

    @tryambak2019

    Thanks Steven Shriver (@stshrive) for documenting this. One question remains unresolved between this issue and #2941.

    Haven Xia (@HavenXia) proposed invoking release-transition validation from bootstrap so Helm and Kustomize upgrades cannot skip it.
    Disabling the entire Job currently turns off two things:

    Could disabled mode skip object creation and updates while still running the read-only safety checks?

  2. QuentinBisson commented on Nov 5, 2026

    @QuentinBisson
    Contributor

    One concern with the ordering line: "Helm runs the namespace, RBAC, and Job as pre-install and pre-upgrade hooks, with weights -10, -5, and 0 and delete policy before-hook-creation."

    As a hook, the Namespace object is deleted before every run. before-hook-creation deletes the object named in the hook's manifest, whoever created it, and waits for the deletion to finish before creating it again. It is also the default when a hook sets no delete policy (hookSetDeletePolicy in pkg/action/hooks.go, Helm 3 and 4). On every helm upgrade, ate-system and podcertificate-controller-system would be deleted with everything in them, including the CA and JWT pools the bootstrap promises to keep. A first install into a cluster where the namespaces already exist would delete them the same way.

    Two shapes avoid it:

    • Create the namespaces from the bootstrap side: a hook Job at an earlier weight that creates each namespace only when it is missing. Its ServiceAccount needs only get and create on namespaces, and the Roles at -5 then have somewhere to go.
    • Ship the namespaces as ordinary release objects with helm.sh/resource-policy: keep in a chart that installs before the bootstrap. Given the stage charts in [EPIC] Helm Chart Support for Substrate Base Installation #3170, agent-substrate-crds or the first stage chart could hold them.

    The first keeps the bootstrap self-contained, and we run it that way today.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions