Skip to content

adiom-data/sample-app

Repository files navigation

sample-app

A Bazel-only sample app with a Go API/auth server, Postgres-backed auth state, a Vite/React SPA, ghcr.io/adiom-data/components/gateway and ghcr.io/adiom-data/components/goosemigrate component images, and Flux OCI bundle publishing through adiom-data/bazel-rules.

Architecture patterns and deployment assumptions are documented in docs/patterns.md. Read that before changing bundle boundaries, database setup, migration Jobs, or gateway health behavior.

Repository layout:

  • cmd/ contains small runnable binary entrypoints.
  • internal/api/ contains the sample API service and binary composition.
  • internal/api/db/ contains API-owned database access helpers.
  • internal/auth/ contains browser auth, token exchange, token issuing, and user authorization.
  • internal/auth/db/ contains auth-owned database access helpers.
  • services/api/migrations/ contains service-owned app/auth database Goose migrations.
  • services/gateway/ contains the gateway image wrapper and config.
  • web/ contains the Vite/React SPA.

Useful targets:

bazel build //cmd/api:image
bazel build //cmd/migrate:image
bazel build //services/gateway:image
bazel build //deploy:infra_deploy
bazel build //deploy:migration_deploy
bazel build //deploy:app_deploy
bazel run //deploy:publish_all
bazel run //deploy:publish_preview_all

The gateway base image is pinned from ghcr.io/adiom-data/components/gateway:v0.0.1 to sha256:2e04c398ee2463d2090ab9ca18d004ee68dcb9336be33ca0ec67aae13494691b. The migration image layers service SQL onto ghcr.io/adiom-data/components/goosemigrate:v0.0.1, pinned to sha256:4728e49ee1c35474aab45218cc8b589667a7853d7a94475c97883268ae2aaf46.

For browser OIDC auth, configure AUTH_ISSUER, OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, AUTH_PRIVATE_KEY_BASE64, and AUTH_STATE_KEY_BASE64. The API fails startup when required auth configuration is missing or invalid. Preview environments may set the full provider callback URL with PROXY_REDIRECT_URL. For native/mobile credential exchange, expose adiom.auth.v1.AuthService and set OIDC_ALLOWED_AUDIENCES to include any additional client IDs that may present provider ID tokens.

Kubernetes secrets are intentionally not checked into deploy/. The deployment environment must create them in the target namespace.

Release environments need one app-owned secret:

  • sample-app-auth
    • Required: OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, AUTH_PRIVATE_KEY_BASE64, AUTH_STATE_KEY_BASE64
    • Optional: OIDC_ALLOWED_AUDIENCES

Release database secrets are generated by CloudNativePG:

  • sample-postgres-app: generated app role credentials used by the API and migration job.
  • sample-postgres-superuser: generated superuser credentials used by the DB setup job.

Preview environments need the same app-owned auth secret:

  • sample-app-auth
    • Required: OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, AUTH_PRIVATE_KEY_BASE64, AUTH_STATE_KEY_BASE64
    • Optional: PROXY_REDIRECT_URL, OIDC_ALLOWED_AUDIENCES

Preview environments also need database credential secrets because preview infra does not create secrets in its deploy bundle:

  • sample-postgres-superuser
    • username: postgres
    • password: empty string
  • sample-postgres-app
    • username: app
    • password: empty string

The app auth secret keys mean:

  • OIDC_ISSUER: OIDC provider issuer URL. For Google, use https://accounts.google.com.
  • OIDC_CLIENT_ID: OAuth/OIDC web client ID from the provider. For Google, this is the client ID ending in .apps.googleusercontent.com.
  • OIDC_CLIENT_SECRET: OAuth/OIDC web client secret from the provider.
  • AUTH_PRIVATE_KEY_BASE64: base64-encoded 32 random bytes. This is the stable Ed25519 seed used to sign app tokens.
  • AUTH_STATE_KEY_BASE64: base64-encoded 32 random bytes. This is the stable browser OAuth state seed used across API replicas.
  • PROXY_REDIRECT_URL: optional full preview proxy callback URL used for browser OAuth redirects.
  • OIDC_ALLOWED_AUDIENCES: optional comma-separated additional OIDC client IDs allowed to exchange provider ID tokens, usually native/mobile client IDs.

For the Google OAuth web client, configure the authorized redirect URI as https://<app-hostname>/auth/callback, or <PROXY_REDIRECT_URL> for preview environments.

Generate the app-owned random secrets as stable values:

# AUTH_PRIVATE_KEY_BASE64, Ed25519 seed: 32 random bytes, standard base64.
openssl rand -base64 32

# AUTH_STATE_KEY_BASE64, browser OAuth state seed: 32 random bytes, standard base64.
openssl rand -base64 32

Both generated values should normally be 44 characters with trailing = padding. Keep them stable across restarts and deploys; regenerating AUTH_PRIVATE_KEY_BASE64 invalidates outstanding app tokens, and regenerating AUTH_STATE_KEY_BASE64 invalidates in-flight browser login callbacks.

AUTH_ISSUER is configured in the deployment manifest for this in-cluster sample; set it to the issuer URL reachable by token verifiers. CloudNativePG bootstraps a dummy bootstrap database owned by app and generates the sample-postgres-app secret. The app role is not a superuser and does not get CREATEDB. The infra setup job uses the stock Postgres image and psql with CNPG's generated sample-postgres-superuser secret to create the real app database owned by app.

Protobuf and Connect stubs are generated with Buf:

buf generate

The gateway validates app tokens from the API auth issuer and forwards the verified bearer token to the BFF API unchanged (auth_forwarding: "verified_bearer"), so it does not need its own internal-JWT signing key. The API also verifies forwarded app tokens through the auth issuer's metadata and JWKS; verifier discovery is lazy so a new pod does not need to call its own Service before it starts serving.

Deployment is split into three Flux OCI bundles:

  • //deploy:infra_deploy owns the CloudNativePG cluster.
  • //deploy:preview_infra_deploy owns a disposable Postgres Deployment backed by emptyDir for preview environments.
  • //deploy:migration_deploy runs lightweight database setup jobs and app database Goose migrations. It is marked force = True.
  • //deploy:app_deploy owns the gateway and API workloads.

//deploy:publish_preview_all uses preview_infra_deploy instead of infra_deploy so preview environments avoid per-preview CNPG PVCs. The deployment environment still provides the DB credential secrets consumed by the shared migration and app bundles.

This repository is intended to be a canonical sample rather than an in-place database upgrade recipe. If you previously deployed the older shape where CNPG bootstrapped the app database directly, recreate the sample Postgres cluster and its PVCs before applying the new bundle sequence.

The API and gateway Kubernetes probes use framework gRPC health services. The API does not gate readiness on transient database availability; database errors are handled at request time so the service can recover when Postgres returns.

The checked-in Kubernetes resources intentionally omit metadata.namespace and HTTPRoute.hostnames, so the bundle can be applied to any namespace and bound to hostnames by the environment. If PROXY_REDIRECT_URL is set, browser auth uses it as the stable provider callback URL in preview environments while the framework stores the final app callback URL in OAuth state. Otherwise PUBLIC_BASE_URL is used when set. If neither value is set, browser auth uses the framework request-scoped redirect resolver to derive its OAuth redirect base URL from forwarded request host/proto headers; auth requests fail if the gateway does not provide a usable host.

Observability is provider-owned. Framework telemetry emits traces and metrics to the namespace collector default http://otel-collector:4318. The sample deployment sets OTEL_SERVICE_NAME=sample-api; application logs remain structured stdout/stderr and are collected by the provider's Kubernetes log pipeline.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors