Skip to content

[WIP] Sentinel v5#53

Open
BK1031 wants to merge 76 commits intomainfrom
v5
Open

[WIP] Sentinel v5#53
BK1031 wants to merge 76 commits intomainfrom
v5

Conversation

@BK1031
Copy link
Copy Markdown
Contributor

@BK1031 BK1031 commented Jan 17, 2026

Sentinel v5 rewrite, see design doc here for more info.

@netlify
Copy link
Copy Markdown

netlify Bot commented Jan 17, 2026

Deploy Preview for gr-sentinel failed. Why did it fail? →

Name Link
🔨 Latest commit aba3aac
🔍 Latest deploy log https://app.netlify.com/projects/gr-sentinel/deploys/696b40e767b51e0008505343

@netlify
Copy link
Copy Markdown

netlify Bot commented Feb 24, 2026

Deploy Preview for gr-sentinel failed. Why did it fail? →

Name Link
🔨 Latest commit 9f11928
🔍 Latest deploy log https://app.netlify.com/projects/gr-sentinel/deploys/69ee93d4ad8b20000893a8ee

BK1031 and others added 24 commits February 24, 2026 01:23
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
BK1031 and others added 30 commits April 5, 2026 19:31
Rincon normalizes display names to slugs on registration, so passing
the display form here keeps the source config readable without affecting
runtime behavior.
The previous implementation passed the zero-value ID from a shadowed
variable into CreateEntity, which made every restart generate a fresh
ULID and leak orphan entity rows. Pass the SentinelCoreEntityID constant
so the default entity is idempotent across restarts.
Move POST /applications/verify under the /core namespace since it is
an internal service-to-service endpoint, not part of the public
applications CRUD surface.

Add GET /core/entity/:entityID and GET /core/entity/:entityID/groups
so oauth (and other internal callers) can resolve entity type, linked
user/service-account ids, and group memberships without hitting core's
database directly.
Resolve entity type, linked user_id or service_account_id, and group
membership via core's new /core/entity/:id endpoints before each token
is signed, and pass them through as JWT claims. Downstream services can
now gate on group membership locally without a round-trip per request.

Claim building lives in oauth/service/token_claims.go as a seam for
future per-app and per-entity overrides.

Make ACCESS_TOKEN_TTL and REFRESH_TOKEN_TTL env-configurable (defaults
30m / 7d), shortening the access-token lifetime reduces how long stale
group membership can linger in a JWT. Invalid values log at warn; unset
values log at info and use the default.
Bootstrap the v5 web client with Vite 8, React 19, TypeScript 6,
Tailwind v4 (via @tailwindcss/vite), shadcn/ui (Nova/radix preset),
react-router-dom 7, and axios. Includes the @/ path alias, configured
axios instances for core and oauth with env-driven base URLs, and
placeholder pages for home, login, the OAuth consent screen, and
applications. No business logic yet — this lands the scaffold only.
Runs node:22-alpine with npm install + npm run dev --host 0.0.0.0 on
port 5173. Source is bind-mounted for HMR, node_modules lives in a
named volume to keep restarts fast. VITE_CORE_API_URL and
VITE_OAUTH_API_URL are set to localhost since the SPA is evaluated in
the browser, not in the container.
Add kerbecs as a single-port edge proxy on :10310 routing to internal
services by path:

  /api/core/*, /api/users/*, /api/applications/*, /api/groups/*  -> core
  /api/oauth/*                                                   -> oauth
  /api/discord/*                                                 -> discord
  /*                                                             -> web

All backend routes use rewrite.strip_prefix: /api so services keep
their existing route registrations. Envelope is passthrough on every
route since OAuth response shapes are spec-defined and the SPA needs
to stream HMR. CORS is enabled with a permissive dev allowlist.

Drops host port mappings from core, oauth, and discord — they're now
reachable only through the gateway. db, web, and rincon stay exposed
for direct dev access (psql, npm run dev fallback, registry inspection).

Frontend collapses VITE_CORE_API_URL + VITE_OAUTH_API_URL to a single
VITE_API_URL pointing at the gateway, and web/src/lib/api.ts exports
one axios instance with baseURL ending in /api.
Scaffold the v5 web app's design surface end-to-end with mock data so
the layout and pages can be iterated on before any business logic
lands.

Layout
- AppShell wraps a sidebar + scrollable inset; sidebar is static
  (collapsible none) since collapse adds drag-handle UX we don't want.
- AppHeader is a slim translucent bar inside the inset with just the
  user dropdown — page chrome lives in the sidebar instead.
- AppSidebar shows a gradient brand mark, Dashboard / Applications /
  Groups / Analytics / Settings nav, and a footer caption.
- AppFooter ports the v4 design (GR logo, gradient hairline divider,
  copyright, social links) with inline brand SVGs in
  components/icons/socials.tsx so we don't pull a brand-icon dep.
- PageContainer + PageHeader primitives in components/PageContainer.tsx
  remove the duplicated max-w-6xl / title-and-description pattern across
  every page.

Pages
- HomePage: greeting, "Recently accessed" apps grid (sorted by
  lastAccessedAt), and a recent-activity card. Mock data only.
- Applications, Groups, Analytics, Settings: stubs using PageHeader
  plus a "Coming soon" card.
- Login, Authorize, NotFound stay full-bleed and don't use AppShell.

Brand + theme
- Geist Variable from shadcn's Nova preset, dark mode forced via
  <html class="dark">.
- gr-pink, gr-purple, discord-blurple live in src/styles/brand.css as
  Tailwind v4 @theme tokens, imported separately from index.css so a
  future shadcn re-init can rewrite the theme file without touching
  brand.

Mock data in src/lib/mock.ts is the single seam to replace once API
calls are wired up.
Header
- HeaderSearch: input-shaped trigger that opens a shadcn CommandDialog
  (cmd+k or click) with grouped results across applications, groups,
  and members. Patches a bug in shadcn's generated CommandDialog where
  it forgot to wrap children in <Command>, which crashed cmdk on open.
- HeaderNotifications: bell icon with an unread dot, dropdown listing
  notifications with type icons, body text, relative timestamps, and a
  "Mark all read" action.
- HeaderUserMenu extracted in-file for symmetry; sideOffset bumped on
  both header dropdowns so they don't sit on top of their triggers.

Footer
- Version pulled live from /api/core/ping and parsed out of the
  message string. Single source of truth — bumping Version in
  core/config/config.go now flows through to the UI.
- Wordmark uses a new font-brand token (Inter Variable) to match the
  Gaucho Racing logo guidelines while body text stays Geist.

Layout / theming
- AppHeader: shrink-0 so flex-col children don't collapse it below h-14.
- brand.css: re-stating --font-sans inside its @theme to keep Geist
  pinned (re-importing tailwindcss in a second CSS file resets the
  default tokens).

Mock data
- mockMembers and mockNotifications added in src/lib/mock.ts as the
  search and notifications data sources until the API lands.

Assets
- Full set of Gaucho Racing logo PNGs copied into web/public/logo
  (car, mechanic variants, wordmark variants).
Login page (web/src/pages/auth/LoginPage.tsx)
- Email + password placeholders (sr-only labels), Google + Discord
  social buttons, OR divider, footer link to the Discord invite for
  account requests.
- Per-button loading state ("email" | "google" | "discord" | null) —
  only the clicked button spins, the others disable while busy.
- Success transition: login widget converges (scale + fade), an
  animated checkmark draws in (circle stroke then check stroke) using
  the gr-pink -> gr-purple gradient via SVG linearGradient, brief hold,
  then document.startViewTransition cross-fades to the dashboard.

Components
- OutlineButton: gradient-bordered button matching v4 with a loading
  prop that swaps children for a spinner (web/src/components/OutlineButton.tsx).
- SuccessCheck: animated SVG check used by the login transition; uses
  useId so the gradient def has a unique, css-safe id
  (web/src/components/SuccessCheck.tsx).
- Google + Discord brand SVG icons added to socials.tsx.
- Label primitive added via shadcn for accessible sr-only labels.

Debug index
- New /debug page lists every route in the app grouped as Dashboard /
  Auth flow / Edge cases, with sample query params on the OAuth
  authorize link (web/src/pages/debug/DebugPage.tsx).
- Added Bug nav item to the sidebar.

Misc
- Discord invite URL surfaced as DISCORD_INVITE_URL in lib/links.ts.
- @Keyframes draw-stroke + a global cursor-pointer rule for buttons
  in styles/brand.css.
Replace the AuthorizePage stub with a real consent screen styled to
match the login page (centered standalone content, no card chrome so
the OutlineButton's bg-background sits flush with the page).

- App heading: gradient icon tile + name + "wants to access your
  Sentinel account". Falls back to an "Unknown application" state when
  the client_id doesn't resolve.
- "Signed in as" block reuses mockUser.
- "This will let X:" lists each requested scope with a friendly label,
  description, and lucide icon. Unknown scopes degrade gracefully and
  show the raw key in mono. Scope metadata lives in
  src/lib/scopes.ts so labels stay consistent if the consent ever needs
  to render outside this page.
- Redirect note shows where the user will land.
- Cancel + Authorize buttons share h-10 / rounded-xl so the shadcn
  outline button visually matches the OutlineButton next to it; each
  has its own loading state.
- Approve triggers the same SuccessCheck transition as login (converge
  + animated gradient checkmark + hold) before redirecting to
  redirect_uri?code=mock_authorization_code. Cancel goes to
  redirect_uri?error=access_denied.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant