14 KiB
Stack Research
Domain: Multi-user gear management platform (v2.0 platform additions) Researched: 2026-04-03 Confidence: MEDIUM-HIGH
This document covers ONLY the new stack additions for v2.0. The existing stack (React 19, Hono, Drizzle ORM, TanStack Router/Query, Tailwind CSS v4, Lucide React, Recharts, framer-motion, Zustand, Zod, Bun) is validated and unchanged.
Recommended Stack
Authentication -- Logto (Self-Hosted)
| Technology | Version | Purpose | Why Recommended |
|---|---|---|---|
| Logto OSS | v1.36+ | External OIDC/OAuth 2.1 auth provider | TypeScript-native, purpose-built for app auth (not enterprise IAM), requires Postgres (shared infra), beautiful pre-built sign-in UI, React SDK with hooks, lightweight JWT validation on backend. MIT-licensed core. |
| @logto/react | ^4.0.13 | React SDK for auth flows | LogtoProvider wraps app, provides useLogto() hook for sign-in/sign-out/token access. Handles OIDC redirect flow, token refresh, and user info. |
| jose | ^6.2.2 | JWT validation on Hono backend | Zero-dependency, Bun-compatible, used to verify Logto-issued access tokens via JWKS. Recommended by Logto docs over heavier alternatives. |
Why Logto over alternatives:
| Provider | Why Not |
|---|---|
| Authentik | Python-based, heavyweight (designed for enterprise proxy/SSO), overkill for app-level auth. No React SDK -- requires raw OIDC integration. Better for infra-level SSO (Portainer, Grafana). |
| Zitadel | Go-based, Kubernetes-first architecture, AGPL 3.0 license (copyleft since 2025). Stronger for multi-tenant B2B SaaS. Over-engineered for a single-product platform. |
| SuperTokens | Session-based by default (not OIDC), requires embedding their middleware into your backend. Tighter coupling than external provider model. |
| Keycloak | Java-based, heavy memory footprint (1-2GB RAM), complex admin UI. Industry standard but vastly over-scoped for this use case. |
Integration pattern: Logto runs as a separate Docker container alongside Postgres. React app redirects to Logto's hosted sign-in page for auth flows. Hono backend validates JWT access tokens from the Authorization header using jose JWKS verification -- no Logto SDK needed on the backend, just standard OIDC token validation. User identity is the Logto sub claim (a stable string ID), stored as userId on all user-owned records.
Backend middleware pattern (Hono):
import { createRemoteJWKSet, jwtVerify } from "jose";
const jwks = createRemoteJWKSet(
new URL("https://logto.example.com/oidc/jwks")
);
const authMiddleware = createMiddleware(async (c, next) => {
const token = c.req.header("Authorization")?.replace("Bearer ", "");
if (!token) return c.json({ error: "Unauthorized" }, 401);
const { payload } = await jwtVerify(token, jwks, {
issuer: "https://logto.example.com/oidc",
audience: "your-api-resource-indicator",
});
c.set("userId", payload.sub);
await next();
});
React provider pattern:
import { LogtoProvider, LogtoConfig } from "@logto/react";
const config: LogtoConfig = {
endpoint: "https://logto.example.com",
appId: "<your-app-id>",
resources: ["https://api.gearbox.example.com"],
};
// Wrap app root
<LogtoProvider config={config}>
<App />
</LogtoProvider>
Database -- PostgreSQL via Bun Native Driver
| Technology | Version | Purpose | Why Recommended |
|---|---|---|---|
| PostgreSQL | 16+ | Primary database | Required by Logto anyway, proper concurrent access for multi-user, JSONB for flexible spec fields, full-text search for discovery feed. |
| drizzle-orm | ^0.45.1 (existing) | Type-safe ORM | Already in use. Switch from drizzle-orm/bun-sqlite to drizzle-orm/bun-sql for Postgres. Schema definitions move from sqlite-core to pg-core. |
| Bun native SQL | built-in | Postgres driver | Zero additional dependencies. import { SQL } from "bun" provides native Postgres bindings. Drizzle ORM supports it via drizzle-orm/bun-sql. |
| postgres (postgres.js) | ^3.4.8 | Fallback Postgres driver | Only needed if Bun native SQL has issues with drizzle-kit CLI tooling (known issue #4122). More mature ecosystem, proven with Drizzle. Install as dev dependency for drizzle-kit. |
Schema migration approach:
- Rewrite
src/db/schema.tsimports fromdrizzle-orm/sqlite-coretodrizzle-orm/pg-core - Replace
sqliteTablewithpgTable - Replace
integer().primaryKey({ autoIncrement: true })withinteger().primaryKey().generatedAlwaysAsIdentity()for PKs - Replace
integer("created_at", { mode: "timestamp" })withtimestamp("created_at").defaultNow().notNull() - Add
userId text("user_id").notNull()to all user-owned tables (items, threads, setups, categories) - Add
visibility text("visibility").notNull().default("private")to setups and profiles - Generate fresh Postgres migration with
drizzle-kit generate - Write a one-time data migration script (SQLite read -> Postgres insert) for existing data
drizzle.config.ts change:
// Before
{ dialect: "sqlite", dbCredentials: { url: "./gearbox.db" } }
// After
{ dialect: "postgresql", dbCredentials: { url: process.env.DATABASE_URL } }
Known issue: drizzle-kit CLI does not use the Bun SQL driver for push/generate commands (GitHub issue #4122). Workaround: install postgres (postgres.js) as a dev dependency for drizzle-kit, while the app runtime uses Bun native SQL.
Image Storage -- Bun Native S3 + MinIO
| Technology | Version | Purpose | Why Recommended |
|---|---|---|---|
| Bun S3Client | built-in | S3 API client | Zero dependencies, native Bun bindings, extends Blob interface. Supports presigned URLs, streaming uploads. Built-in MinIO compatibility. |
| MinIO | latest | Self-hosted S3-compatible object storage | Replaces local ./uploads/ directory. Single Go binary, Docker-friendly, S3 API compatible. Handles multi-user image scaling without cloud vendor lock-in. |
Why Bun native S3 over @aws-sdk/client-s3:
- Zero additional dependencies (Bun ships with it)
- Simpler API (extends Blob, web-standard patterns)
- Native performance bindings
- Full MinIO compatibility documented by Bun team
Migration from ./uploads/:
- Deploy MinIO container alongside app
- Create
gearbox-imagesbucket - Write migration script to upload existing files from
./uploads/to MinIO - Update image service to use S3Client for reads/writes
- Serve images via presigned URLs or a proxy route on Hono
Configuration:
import { S3Client } from "bun";
const storage = new S3Client({
accessKeyId: process.env.S3_ACCESS_KEY!,
secretAccessKey: process.env.S3_SECRET_KEY!,
bucket: "gearbox-images",
endpoint: process.env.S3_ENDPOINT!, // e.g., http://minio:9000
});
Supporting Libraries
| Library | Version | Purpose | When to Use |
|---|---|---|---|
| jose | ^6.2.2 | JWKS-based JWT verification | Every authenticated API request -- validate Logto access tokens on Hono middleware |
| @logto/react | ^4.0.13 | React auth provider + hooks | Wrap app root, sign-in/sign-out flows, access token retrieval for API calls |
Development / Infrastructure
| Tool | Purpose | Notes |
|---|---|---|
| Docker Compose | Local dev environment | Postgres + Logto + MinIO containers. App still runs on bare Bun for HMR. |
| drizzle-kit | Schema management | Same tool, different dialect config. bun run db:generate and bun run db:push still work. |
Installation
# New production dependencies
bun add @logto/react jose
# New dev dependencies (for drizzle-kit Postgres support)
bun add -D postgres
# No install needed for:
# - Bun native S3 (built-in)
# - Bun native SQL/Postgres (built-in)
# - drizzle-orm (already installed, just change imports)
Alternatives Considered
Authentication Provider
| Recommended | Alternative | When to Use Alternative |
|---|---|---|
| Logto | Authentik | If you need proxy-mode SSO for non-OIDC apps (Portainer, legacy tools) |
| Logto | Zitadel | If building multi-tenant B2B SaaS with organization-level isolation |
| Logto | Keycloak | If enterprise LDAP/AD integration is mandatory |
Database Driver
| Recommended | Alternative | When to Use Alternative |
|---|---|---|
Bun native SQL (bun:sql) |
postgres.js | If Bun native SQL has concurrency bugs (known issue in Bun 1.2.0 with concurrent statements) |
Bun native SQL (bun:sql) |
@neondatabase/serverless | If deploying to serverless/edge where persistent connections are not possible |
Image Storage
| Recommended | Alternative | When to Use Alternative |
|---|---|---|
| MinIO (self-hosted) | Cloudflare R2 | If you want zero-ops storage with no egress fees and don't mind cloud dependency |
| MinIO (self-hosted) | Local filesystem (current) | For development/testing only. Not viable for multi-user at scale. |
What NOT to Add
| Avoid | Why | Use Instead |
|---|---|---|
| @aws-sdk/client-s3 | 60+ transitive dependencies, Bun has native S3 support | Bun built-in S3Client |
| passport.js / express-session | Wrong paradigm -- we want external OIDC, not embedded session auth | Logto + jose JWT validation |
| next-auth / auth.js | Designed for Next.js, assumes framework integration we don't have | Logto (external provider) |
| better-auth | Embedded auth library, opposite of external provider model | Logto (external provider) |
| pg (node-postgres) | Callback-based API, Bun has native Postgres bindings | Bun native SQL or postgres.js |
| sharp / image processing libs | Premature optimization -- serve originals first, add resizing later if needed | Direct S3 storage of originals |
| Redis | Not needed at this scale. Postgres handles sessions (via Logto), caching is premature | Postgres for everything |
| Prisma | Already using Drizzle ORM, no reason to add a second ORM | drizzle-orm (existing) |
| nanoid / cuid2 | Postgres gen_random_uuid() is built-in for public-facing IDs if needed |
Postgres native UUID generation |
| TypeORM / Sequelize | Legacy ORMs with worse TypeScript support than Drizzle | drizzle-orm (existing) |
Infrastructure Architecture
Docker Compose (dev) / Docker (prod)
+-- gearbox-app (Bun, port 3000)
+-- gearbox-postgres (PostgreSQL 16, port 5432)
| +-- gearbox DB (app data)
| +-- logto DB (Logto data, separate database same instance)
+-- gearbox-logto (Logto OSS, port 3001 app / 3002 admin)
+-- gearbox-minio (MinIO, port 9000 API / 9001 console)
Logto and the app share a single Postgres instance (different databases). This keeps infrastructure simple -- one Postgres to back up, one to monitor. Logto requires PostgreSQL 14+; using 16 covers both.
Version Compatibility
| Package | Compatible With | Notes |
|---|---|---|
| drizzle-orm@0.45.x | Bun native SQL | Supported via drizzle-orm/bun-sql driver |
| drizzle-orm@0.45.x | postgres.js@3.4.x | Supported via drizzle-orm/postgres-js driver (fallback) |
| drizzle-kit@0.31.x | PostgreSQL 16 | Generates Postgres-dialect migrations |
| @logto/react@4.x | React 19 | Uses React context/hooks, compatible |
| jose@6.x | Bun runtime | Explicitly lists Bun support in docs |
| Logto OSS v1.36 | PostgreSQL 14+ | Logto requires PG 14 minimum; use PG 16 for both app and Logto |
| Bun S3Client | MinIO latest | Documented compatibility with endpoint configuration |
Migration Checklist (SQLite to Postgres)
- Schema rewrite:
sqlite-core->pg-coreimports, adjust column types - Driver swap:
drizzle-orm/bun-sqlite->drizzle-orm/bun-sql - Config update:
drizzle.config.tsdialect and credentials - Fresh migrations: Generate from scratch for Postgres (do not try to convert SQLite migrations)
- Data migration: One-time script reads SQLite, writes to Postgres
- Test infrastructure: Update
createTestDb()helper to use Postgres test database (or pg-mem for in-memory testing) - CI pipeline: Add Postgres service container for test runs
- Remove SQLite deps: Remove
better-sqlite3from devDependencies after migration confirmed
Sources
- Logto official docs -- React quickstart -- SDK setup, LogtoProvider config (HIGH confidence)
- Logto API protection -- JWT validation -- jose-based middleware pattern (HIGH confidence)
- Logto OSS getting started -- Docker deployment, Postgres requirements (HIGH confidence)
- Logto @logto/react npm -- Version 4.0.13 confirmed (HIGH confidence)
- Drizzle ORM -- Bun SQL driver -- Native Postgres via Bun (HIGH confidence)
- Drizzle ORM -- PostgreSQL column types -- pg-core schema definitions (HIGH confidence)
- drizzle-kit Bun SQL issue #4122 -- Known CLI limitation with Bun driver (MEDIUM confidence)
- Bun S3 documentation -- Native S3 client, MinIO config (HIGH confidence)
- MinIO GitHub -- S3-compatible self-hosted storage (HIGH confidence)
- jose GitHub -- JWT library v6.2.2, explicit Bun support (HIGH confidence)
- Authentik vs Zitadel comparison -- Auth provider analysis (MEDIUM confidence)
- Keycloak vs Authentik vs Zitadel 2026 -- Ecosystem overview (MEDIUM confidence)
- postgres.js npm -- Version 3.4.8, fallback driver (HIGH confidence)
Stack research for: GearBox v2.0 Platform Foundation Researched: 2026-04-03