{"id":50886112,"url":"https://github.com/hops-ops/auth-stack","last_synced_at":"2026-06-15T17:02:39.600Z","repository":{"id":357913992,"uuid":"1238265442","full_name":"hops-ops/auth-stack","owner":"hops-ops","description":"AuthStack — Crossplane Configuration wrapping Zitadel with durable+operational secrets pattern","archived":false,"fork":false,"pushed_at":"2026-05-22T22:03:43.000Z","size":97,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-23T00:07:43.492Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go Template","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hops-ops.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-14T01:07:25.000Z","updated_at":"2026-05-22T22:02:08.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/hops-ops/auth-stack","commit_stats":null,"previous_names":["hops-ops/auth-stack"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/hops-ops/auth-stack","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hops-ops%2Fauth-stack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hops-ops%2Fauth-stack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hops-ops%2Fauth-stack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hops-ops%2Fauth-stack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hops-ops","download_url":"https://codeload.github.com/hops-ops/auth-stack/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hops-ops%2Fauth-stack/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34372130,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-15T02:00:07.085Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-15T17:02:38.563Z","updated_at":"2026-06-15T17:02:39.591Z","avatar_url":"https://github.com/hops-ops.png","language":"Go Template","funding_links":[],"categories":[],"sub_categories":[],"readme":"# auth-stack\n\nInstalls Zitadel into a Kubernetes cluster as the platform identity provider, and hosts a focused set of `auth.hops.ops.com.ai` primitive XRDs (`HumanUser`, `MachineUser`, `Grant`, `IDP`) that compose against the installed Zitadel.\n\nThe stack XRD (`AuthStack`) wraps the upstream `zitadel/zitadel` Helm chart — handling the namespace, database wiring, Gateway API routing, and re-projecting chart-managed bootstrap secrets (admin PAT + login-client PAT) into XR status for downstream consumers.\n\nThe primitive XRDs let operators declaratively manage the identity model inside a running Zitadel — see [Auth-group primitives](#auth-group-primitives) below.\n\n## Quick Start\n\nLocal-dev (colima cluster, bundled Postgres, no TLS):\n\n```yaml\napiVersion: hops.ops.com.ai/v1alpha1\nkind: AuthStack\nmetadata:\n  name: auth\n  namespace: default\nspec:\n  clusterName: colima\n  domain: auth.localtest.me\n  externalSecure: false\n  database:\n    bundled: true\n```\n\nProduction (managed Postgres + Gateway API):\n\n```yaml\napiVersion: hops.ops.com.ai/v1alpha1\nkind: AuthStack\nmetadata:\n  name: auth\n  namespace: platform\nspec:\n  clusterName: prod\n  domain: auth.example.com\n  firstInstance:\n    org: hops-ops\n  database:\n    external:\n      dsnSecretRef:\n        name: zitadel-db\n        key: dsn\n  gateway:\n    enabled: true\n    parentRef:\n      name: platform-gateway\n      namespace: istio-system\n```\n\n## Database Modes\n\nExactly one of:\n\n| Mode | When | Notes |\n|---|---|---|\n| `external` | Production with managed Postgres (RDS, CNPG cluster, etc.) | Provide a Secret containing the DSN; chart reads `ZITADEL_DATABASE_POSTGRES_DSN`. |\n| `bundled` | Local dev only | Sets `postgresql.enabled=true` so the chart uses its bundled Bitnami subchart. Not for production. |\n| `psqlStack` | Future | Reserved for the platform PSQLStack integration; pending the `PSQLDatabase` XRD. The composition currently rejects this mode. |\n\n## Bootstrap \u0026 Status Contract\n\nThe `zitadel/zitadel` chart's setup hook creates two machine users on first install:\n\n- `\u003ciamAdmin.username\u003e` — JWT machine key in a chart-managed Secret named after the username\n- `\u003ciamAdmin.username\u003e-pat` — admin PAT\n- `\u003cloginClient.username\u003e` — login-client PAT\n\nThis stack does not author its own init Job. It re-projects those Secret references into XR status so downstream consumers (the future Zitadel Crossplane provider, ad-hoc admin tooling) have a stable place to read the credentials:\n\n```yaml\nstatus:\n  oidc:\n    issuerURL: https://auth.example.com\n    discoveryURL: https://auth.example.com/.well-known/openid-configuration\n  bootstrap:\n    iamAdminPatSecretRef:    { name: iam-admin-pat,  namespace: zitadel, key: pat }\n    iamAdminKeySecretRef:    { name: iam-admin,      namespace: zitadel, key: key }\n    loginClientPatSecretRef: { name: login-client,   namespace: zitadel, key: pat }\n```\n\n## Auth-group primitives\n\nPer [[specs/identity-architecture]], the auth-group primitive XRDs that have substantive composition value-add — `MachineUser`, `Grant` — live in this repo alongside `AuthStack` under the `auth.hops.ops.com.ai` group.\n\nStatus:\n\n| Kind | Plural | Composes | Status |\n|---|---|---|---|\n| `MachineUser` | `machineusers` | `MachineUser` + opt-in `AccessToken` + opt-in AWS SM `Secret` + ESO `PushSecret` (provider-kubernetes Object) | ✓ |\n| `Grant` | `grants` | `user.zitadel.../Grant` (same-Org) or `project.zitadel.../Grant + user.zitadel.../Grant` with `projectGrantId` (cross-Org) | ✓ |\n\nSingle-resource wrappers we deliberately didn't make: `HumanUser`, `IDP`, `OrganizationSsoConfig` (and the previously-attempted `Organization`, `Project`). Operators apply raw Zitadel / OpenPanel MRs directly for those.\n\n### `MachineUser`\n\nDeclarative Zitadel machine identity for CI runners, Crossplane providers, cross-cluster syncs — anything that needs a long-lived credential to call a SaaS API.\n\nThe XRD's value-add is bundling. The `MachineUser` MR alone is one Zitadel resource. With `spec.pat.enabled: true` it adds an `AccessToken` MR (the PAT). With `spec.pat.pushToAwsSm: true` it adds an AWS Secrets Manager `Secret` MR + an ESO `PushSecret` Kubernetes Object — pushing the control-plane connection secret's `access_token` into AWS SM at the canonical path `push/\u003ccluster\u003e/\u003ctenant\u003e/\u003cname\u003e` per [[reference_aws_sm_push_tag_convention]]. Four resources, one declarative flag.\n\nPAT generation is **opt-in by default** — `pat.enabled: false` means no long-lived token is minted. Adoption of an existing Zitadel machine user uses `spec.machineUserId` (propagates as `crossplane.io/external-name` on the underlying MR).\n\nSee `examples/machineusers/{minimal,with-pat,with-pat-push}.yaml`.\n\n### `Grant`\n\nFirst-class membership relationship that ties a Zitadel User to a Project + Roles. Polymorphic dispatch — caller writes `userId + userOrgId + projectId + projectOrgId + roles` and the composition picks the right Zitadel mechanism:\n\n- **Same-Org** (`userOrgId == projectOrgId`): composes one `user.zitadel.m.crossplane.io/Grant` MR (the user's role assignment within the project).\n- **Cross-Org** (`userOrgId != projectOrgId`): composes a `project.zitadel.m.crossplane.io/Grant` (cross-Org Project Grant authorizing the role set for the user's home Org) plus a `user.zitadel.m.crossplane.io/Grant` with `projectGrantId` set (the user's role assignment, pulling roles from the granted set). Multi-iter: user/Grant emits once project/Grant is observed.\n\nSee `examples/grants/{same-org,cross-org}.yaml`.\n\n## Cross-Stack Integration\n\nThe intent is for consumer stacks (gitops/ArgoCD, observe/Grafana, the-website) to wire to AuthStack's status surface rather than configuring OIDC manually. Today, those consumers still need a Zitadel OIDC application created out-of-band (via the Zitadel UI/API) and a client ID/secret provided to them. Once the Zitadel Crossplane provider lands, consumer stacks can declaratively create OIDC applications by referencing `status.bootstrap.iamAdminPatSecretRef`.\n\nSee [[specs/auth-stack-zitadel]] for the design and open questions.\n\n## Out of Scope\n\n- Per-app OIDC client creation (lives with the Zitadel API or the future Zitadel Crossplane provider).\n- Istio `RequestAuthentication` / `AuthorizationPolicy` (per-app concern, may land later).\n- Authentik decommission (per-consumer migration tracked separately).\n\n## References\n\n- Spec: `[[specs/auth-stack-zitadel]]`\n- Task: `[[tasks/auth-stack]]`\n- Upstream chart: `zitadel/zitadel` 9.34.1 (ships Zitadel v4)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhops-ops%2Fauth-stack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhops-ops%2Fauth-stack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhops-ops%2Fauth-stack/lists"}