Add configurable OIDC login and Atlas-style shell - #1
Merged
Conversation
pepicrft
force-pushed
the
feat/replicate-fabrik-login-layout
branch
from
May 28, 2026 13:06
23b85ac to
a462d73
Compare
pepicrft
force-pushed
the
feat/replicate-fabrik-login-layout
branch
from
May 28, 2026 13:39
a462d73 to
1aec247
Compare
Introduce a Layouts module that owns the app shell (root HTML, dashboard chrome with headerbar + sidebar + content area) so pages render content into a slot instead of inlining the full chrome. Simplify the overview page to a title, subtitle, and Getting started card — drop the hero, 3-column metric grid, and Workspace badge that didn't match the design direction. Ship a honeycomb logo asset at priv/static/images/logo.png, wire it as the headerbar logo, the login frame logo, and the favicon, and feature it at the top of README.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Match the login frame typography, sizing, and background to tuist's auth page: heading-medium title (instead of display-small), body-small centered subtitle, 50×50 logo, and large blurred purple oklch gradients translated into the top-right and bottom-left corners. Tighten the frame with overflow:hidden and a max-width clamp so it never exceeds the viewport on small screens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Generalize the auth controller into a provider-keyed start/callback flow at /auth/:provider so any number of OIDC providers can coexist on the login screen. Move provider configuration into Hive.Auth.providers/0, which returns a list of configured provider maps; the login page renders one button per provider. Add Google as a first-class provider with hardcoded endpoints (accounts.google.com, oauth2.googleapis.com, openidconnect.googleapis.com), driven by HIVE_GOOGLE_CLIENT_ID / HIVE_GOOGLE_CLIENT_SECRET. Add an email-domain allowlist via HIVE_GOOGLE_ALLOWED_DOMAINS (also HIVE_OIDC_ALLOWED_DOMAINS) enforced on the callback, plus Google's `hd=` hint on the authorize redirect when a single domain is set. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Strip Tuist-specific assumptions from the default values so the chart is usable for any Hive deployment. Defaults now leave host, image pull secret, External Secrets Operator integration, and the Postgres S3 backup unconfigured. A new `secrets.existingSecret` value lets operators bring their own Kubernetes Secret. Move Tuist's production configuration into values-production.yaml: hive.tuist.dev host, HIVE_AUTH_MODE=oidc with HIVE_GOOGLE_ALLOWED_DOMAINS, ghcr-pull image pull secret, externalSecrets with the onepassword-hive ClusterSecretStore and 1Password remote refs for SECRET_KEY_BASE and the Google OAuth credentials, hcloud-volumes storage, and the S3 backup endpoint. The deploy workflow applies this overlay via `helm upgrade --values`. `helm template hive infra/helm/hive` now renders a generic-only set of resources with zero references to Tuist, 1Password, or hcloud. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the placeholder "Phoenix application for orchestrating product work" intro with a storytelling lede that frames Hive as Tuist's take on shaping and building product with LLMs and agentic workflows, designed for internal use and equally for opening up to the people who use the deployer's products. Document the new multi-provider auth (Google as a first-class option with allowed-domains, plus the generic OIDC fallback) and the split between the generic Helm chart and the Tuist production overlay, including a copy-pasteable bring-your-own-secret recipe. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Capture project orientation for agentic tools: tech stack, layout, setup, common commands, auth model, conventions, and deployment. Symlink CLAUDE.md → AGENTS.md so both conventions point at the same source of truth (relative target works in any clone). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…minator Drop the parallel HIVE_GOOGLE_* config namespace in favor of a single HIVE_OIDC_* surface with a HIVE_OIDC_PROVIDER=google|generic discriminator. When set to google, Hive injects Google's hardcoded authorize/token/userinfo URLs and applies the hd= authorize hint; otherwise the operator supplies the URLs themselves. Hive.Auth now exposes a single provider/0 instead of a list-returning providers/0 — one provider per instance is the only supported shape, which matches how operators actually deploy it. The login page renders one button, the controller flow at /auth/:provider is unchanged. The Tuist production overlay sets HIVE_OIDC_PROVIDER=google and wires the 1Password hive-google-oauth/username|credential refs into HIVE_OIDC_CLIENT_ID/HIVE_OIDC_CLIENT_SECRET. No change required in Google Cloud Console (callback URL is still /auth/google/callback) or in 1Password (item name unchanged). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hive is the product, not a rebrand surface. Drop the HIVE_PRODUCT_NAME, HIVE_PRODUCT_TAGLINE, and HIVE_AUTH_PROVIDER_NAME env vars; hardcode "Hive" in the one place it appears and remove the "Product work orchestration" tagline that was placeholder copy on both the login subtitle and the overview page. The generic-OIDC button label falls back to a fixed "Identity provider" string — operators who use HIVE_OIDC_PROVIDER=google get a "Google" label either way, which is the realistic path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Break the 431-line app.css monolith into co-located files: assets/css/ ├── app.css # imports + base resets only ├── layouts/dashboard.css # .layout, .headerbar, .layout__main, .layout__content ├── components/account_dropdown.css # .account-dropdown (extension of <.avatar>) └── routes/login.css # everything under #login Rewrite every rule with native CSS nesting (& operator) so styles are co-located with the selector they belong to, no utility classes, and all spacing/colors/typography reference Noora variables. Document the convention in AGENTS.md so future routes follow it: routes are identified by an #id, components live in components/, nest with &, prefer Noora vars over hardcoded values. Verified by rebuilding the bundle and screenshotting /login and /: visually identical to before the split. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace BEM-style child classes (.headerbar__left, .headerbar__right,
.headerbar__brand, .headerbar__title, .layout__main, .layout__content)
with the data-part pattern that the Tuist UI codebase uses
(../tuist/server, ../atlas). Each layout/component has one anchor
class (.layout, .headerbar) and every internal region is addressed
via data-part="name" on the HTML, with CSS nested under the anchor:
.headerbar {
& [data-part="left-section"] { ... }
& [data-part="right-section"] { ... }
& [data-part="title"] { ... }
}
Update HEEx in components/layouts.ex to swap class names for
data-part attributes. Drop the orphan [data-part="line-divider"]
rule — Noora's <.line_divider /> component renders its own styling
under .noora-line-divider and no longer needs an override.
Promote the rule to a prominent paragraph in AGENTS.md so future
work follows it from the start. Also add a security note: Hive is
an open-source repo, so credentials never go in source, fixtures,
commit messages, or PR bodies — production secrets live in
1Password and are pulled in via External Secrets at deploy time.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…oidcc Delete the bespoke PKCE/state/token-exchange code in AuthController and the provider-config plumbing in Hive.Auth in favor of Ueberauth and ueberauth_oidcc. Both Google and a generic OIDC provider are configured as Ueberauth strategies pointed at distinct issuers; ueberauth_oidcc handles discovery via .well-known/openid-configuration, state, PKCE, token exchange, and userinfo. `config/runtime.exs` reads env vars, conditionally registers issuers + strategies, and stores display metadata (button label, allowed domains) under :hive, :auth, :providers. Multiple providers can run simultaneously and show up as separate buttons on the login screen. Hive.Auth shrinks to four helpers: enabled?/0, providers/0, check_domain/2 (callback-side allowlist enforcement), and current_user/1. The router pipes /auth/:provider and /auth/:provider/callback through a dedicated :oauth pipeline running the Ueberauth plug; AuthController.callback/2 reads conn.assigns.ueberauth_auth, applies the domain check, and puts the user in the session. Env vars revert to per-provider namespaces since strategies are independent: HIVE_GOOGLE_CLIENT_ID, HIVE_GOOGLE_CLIENT_SECRET, HIVE_GOOGLE_ALLOWED_DOMAINS HIVE_OIDC_ISSUER, HIVE_OIDC_CLIENT_ID, HIVE_OIDC_CLIENT_SECRET, HIVE_OIDC_DISPLAY_NAME, HIVE_OIDC_ALLOWED_DOMAINS HIVE_OIDC_PROVIDER, HIVE_OIDC_AUTHORIZE_URL, HIVE_OIDC_TOKEN_URL, HIVE_OIDC_USERINFO_URL, HIVE_*_SCOPES, and HIVE_AUTH_PROVIDER_NAME go away — the issuer URL is sufficient for OIDC discovery, scopes are fixed at openid+profile+email, and display name lives under HIVE_OIDC_DISPLAY_NAME. The Tuist production overlay renames its two ExternalSecret keys from HIVE_OIDC_* back to HIVE_GOOGLE_*; the 1Password item itself (hive-google-oauth) is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Have `Hive.Auth.check_domain/2` accept the provider map (or `nil`)
directly instead of looking up the provider by key from
`Application.get_env`. The function is now pure — pattern-matches
on `%{allowed_domains: ...}` and returns `:ok` or
`{:error, :domain_not_allowed}` without touching application state.
The controller does the lookup itself before calling
`check_domain`. Hive.AuthTest runs `async: true`, passes provider
maps in directly, and no longer needs the `Application.put_env`
setup/`on_exit` dance. Also covers case-insensitive domain
matching, which the old test set missed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduce a HIVE_VISIBILITY env var (`public` default, or `private`) that controls whether `HiveWeb.Plugs.RequireAuthenticated` gates the dashboard. `public` lets everyone reach the dashboard without signing in; `private` redirects unauthenticated requests to /login. Login itself stays available regardless of visibility so admins can sign in to a public instance. Replaces HIVE_AUTH_MODE: the old `none`/`oidc` knob conflated "is auth available?" with "is auth required?". Visibility now controls only the gate; provider configuration (HIVE_GOOGLE_*, HIVE_OIDC_*) decides what sign-in options exist. Hive.Auth's surface shrinks: `enabled?/0` and `mode/0` are gone, replaced by `visibility/0`, `private?/0`, `public?/0`. The plug and the login page use `private?/0`. Add `HiveWeb.Plugs.RequireAuthenticatedTest` exercising the plug end-to-end against the visibility config (4 cases: public passes, default-public passes, private without session redirects, private with session passes). Also clean up the README: - Drop all em dashes (replace with commas/colons/periods). - Move the Tuist-specific production-setup section out of the public README and into AGENTS.md, where internal contributors can still find the 1Password vault contents, ClusterSecretStore bootstrap script, and cluster assumptions. The README now only documents the generic chart usage. Tuist's production overlay is set to `HIVE_VISIBILITY: "public"` since hive.tuist.dev is meant to be open to anyone; the Google allowlist (HIVE_GOOGLE_ALLOWED_DOMAINS=tuist.dev) still restricts who can actually sign in. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…and Helm chart Adapt the tuist/tuist monorepo release model to Hive. Versioning is driven by Conventional Commits + git-cliff on two independent tracks: - App releases tag `server@X.Y.Z` and push `ghcr.io/tuist/hive:X.Y.Z` (plus `:latest`). Driven by `cliff.toml` at the repo root — any Conventional Commit that isn't `(helm)`-scoped contributes, so `feat:`, `feat(auth):`, `refactor(css):` etc. all count toward the next app release. - Helm chart releases tag `helm@X.Y.Z` and push to `oci://ghcr.io/tuist/charts/hive`. Driven by `infra/helm/hive/cliff.toml` — only `(helm)`-scoped commits count. `.github/workflows/release.yml` runs on each push to main: check-releases evaluates whether either component has unreleased commits since the last matching tag; release-server and release-helm build artifacts and refresh their CHANGELOGs; commit-and-release tags the commit, publishes GitHub Releases with cliff-generated notes, and commits the CHANGELOG + Chart.yaml bumps back to main. `deploy.yml` is unchanged — it still ships every push to production with a sha-tagged image. The release workflow runs in parallel and produces versioned artifacts for out-of-band consumers. git-cliff is added to mise.toml so contributors can preview release notes locally; AGENTS.md documents the flow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the Application.put_env / setup / on_exit dance in RequireAuthenticatedTest and AuthControllerTest with Mimic stubs. Both files now run `async: true`: stubs apply per-test in the test process only, so the suite can parallelize without races on the shared application env. `test/test_helper.exs` declares Hive.Auth as Mimic-copyable. Tests stub the narrow query helpers (`private?/0`, `current_user/1`, `providers/0`) directly, which is what the code's API was designed for in the first place. Add `:mimic ~> 1.7` as a test-only dep. Also document the rule in AGENTS.md so future tests follow it from the start: no Application config mutation in setup; use Mimic stubs on the wrapping module instead. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CodeQL flagged 8 occurrences of unpinned third-party actions in .github/workflows/release.yml. Pin each to the commit SHA that the tag currently points at, with the version preserved as a trailing comment so renovate/dependabot can keep them current: jdx/mise-action v4.0.1 → 1648a781... docker/setup-buildx-action v3 → 8d2750c6... docker/login-action v3 → c94ce9fb... docker/build-push-action v6 → 10e90e36... softprops/action-gh-release v2 → 3bb12739... Leaving actions/* refs alone — they're maintained by GitHub itself and weren't flagged by CodeQL. The other workflows (hive.yml, deploy.yml) are also unchanged; they pre-existed on main and weren't part of the PR's scan. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Testing
mix format --check-formattedmix testmix assets.buildhelm template hive infra/helm/hive --set image.tag=testPORT=4861 mix phx.serverwith curl checks for/and/login