- TypeScript 96.2%
- CSS 2.8%
- Dockerfile 0.9%
- JavaScript 0.1%
|
All checks were successful
Server CI / lint-and-build (pull_request) Successful in 2m3s
Portfolio CI / lint-and-build (pull_request) Successful in 2m5s
Web CI / lint-and-build (pull_request) Successful in 2m41s
Server CI / build-and-publish (pull_request) Successful in 3m53s
Portfolio CI / build-and-publish (pull_request) Successful in 1m56s
Web CI / build-and-publish (pull_request) Successful in 2m0s
Server CI / lint-and-build (push) Successful in 2m7s
Portfolio CI / lint-and-build (push) Successful in 2m8s
Web CI / lint-and-build (push) Successful in 2m53s
Portfolio CI / build-and-publish (push) Successful in 2m51s
Web CI / build-and-publish (push) Successful in 3m50s
Server CI / build-and-publish (push) Successful in 4m15s
|
||
|---|---|---|
| .agents/skills | ||
| .forgejo/workflows | ||
| .zed | ||
| apps | ||
| packages | ||
| .dockerignore | ||
| .gitattributes | ||
| .gitignore | ||
| .oxfmtrc.json | ||
| .oxlintrc.json | ||
| AGENTS.md | ||
| bts.jsonc | ||
| docker-compose.yml | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| renovate.json | ||
| skills-lock.json | ||
| tsconfig.json | ||
| turbo.json | ||
Mosaic
Mosaic is a self-hosted, authenticated start page for organizing bookmarks into spaces and categories. It includes a searchable bookmark dashboard, favorites, per-user settings, and email/password authentication. The monorepo also contains a standalone personal portfolio site.
Tech stack
- Web: Next.js 16, React 19, Tailwind CSS, shadcn/ui, TanStack Query/Form
- Portfolio: Standalone Next.js 16 portfolio site sharing UI components and theme support
- API: Hono with type-safe oRPC and generated OpenAPI reference
- Data: Drizzle ORM with SQLite or Turso/libSQL
- Authentication: Better Auth (email and password)
- Tooling: TypeScript, pnpm 10, Turborepo, Oxlint, and Oxfmt
- Deployment: Docker Compose, with Forgejo CI publishing separate web and server images
Prerequisites
- Node.js 24 (the Docker images use Node 24)
- pnpm 10.26.0 (Corepack is recommended)
- A SQLite/libSQL database, locally or through Turso
Getting started
Install dependencies from the repository root:
corepack enable
pnpm install
Configure environment variables
Create apps/server/.env:
# `pnpm run db:local` serves a local database on port 8080.
# A remote Turso/libSQL URL works here as well.
DATABASE_URL=http://127.0.0.1:8080
# Generate a unique secret with at least 32 characters.
BETTER_AUTH_SECRET=replace-with-a-secret-of-at-least-32-characters
BETTER_AUTH_URL=http://localhost:3000
CORS_ORIGIN=http://localhost:3001
NODE_ENV=development
Create apps/web/.env:
SERVER_URL=http://localhost:3000
# Set to true or 1 to hide registration.
DISABLE_REGISTRATION=false
BETTER_AUTH_URL,CORS_ORIGIN, andSERVER_URLmust use the URLs that browsers use to access the server and web app. Use HTTPS URLs when deploying; Better Auth sessions are configured as secure cookies.
SERVER_URLis validated server-side and intentionally passed through the root client provider for browser API calls. When running with Docker, configure it with an API origin that is reachable by both the web container and users' browsers.
Initialize the database
For a local Turso development database, start the local service in one terminal:
pnpm run db:local
Then apply the schema:
pnpm run db:push
For a remote Turso/libSQL database, set its URL as DATABASE_URL and run the same db:push command. Use migrations rather than schema push for managed database changes:
pnpm run db:generate
pnpm run db:migrate
Start development servers
pnpm run dev
The web app is available at http://localhost:3001, the API is available at http://localhost:3000, and the portfolio is available at http://localhost:3002. The OpenAPI reference is served under /api-reference.
To run only the portfolio:
pnpm --filter portfolio dev
Application features
- Create and switch between bookmark spaces
- Add, organize, and favorite bookmarks by category
- Search saved bookmarks or send queries to a configurable search engine
- Manage a preferred search-query pattern per user
- Register and sign in with email and password
- Choose between light and dark themes
Project structure
mosaic/
├── apps/
│ ├── web/ # Authenticated Next.js start-page application
│ ├── portfolio/ # Standalone Next.js portfolio site
│ └── server/ # Hono HTTP API and oRPC/OpenAPI handlers
├── packages/
│ ├── api/ # oRPC routers and application logic
│ ├── auth/ # Better Auth configuration
│ ├── config/ # Shared TypeScript configuration
│ ├── db/ # Drizzle schema, migrations, and database client
│ ├── env/ # Typed server and web environment validation
│ └── ui/ # Shared shadcn/ui primitives, hooks, and styles
├── docker-compose.yml
└── turbo.json
Common commands
| Command | Description |
|---|---|
pnpm run dev |
Start the web app and API in development mode. |
pnpm run dev:web |
Start only the Next.js app on port 3001. |
pnpm run dev:server |
Start only the Hono API on port 3000. |
pnpm --filter portfolio dev |
Start only the portfolio site on port 3002. |
pnpm run build |
Build all applications. |
pnpm run check-types |
Check TypeScript types across the workspace. |
pnpm run check |
Run Oxlint and format files with Oxfmt. |
pnpm run db:local |
Start the local Turso development database. |
pnpm run db:push |
Apply the current Drizzle schema directly to the database. |
pnpm run db:generate |
Generate a Drizzle migration. |
pnpm run db:migrate |
Apply generated Drizzle migrations. |
pnpm run db:studio |
Open Drizzle Studio. |
Shared UI components
Reusable UI components live in packages/ui. Update global design tokens in packages/ui/src/styles/globals.css and import components through the package's public exports. Basic shadcn/ui primitives are under components/basic; reusable visual effects and theme components are under components/special and components/theme, respectively:
import { Button } from "@mosaic/ui/components/basic/button";
Add a shared primitive from the repository root:
pnpm dlx shadcn@latest add accordion dialog popover sheet table -c packages/ui
For app-specific blocks, run the shadcn CLI from the consuming app, such as apps/web or apps/portfolio.
Docker Compose deployment
Docker Compose builds and runs separate web and server containers:
pnpm run docker:build
pnpm run docker:up
pnpm run docker:logs
pnpm run docker:down
Compose exposes the web app on port 3001 and the API on port 3000. It optionally loads apps/web/.env and apps/server/.env; the web container uses the server service for server-side requests while browsers use the public API URL baked into the web image. The server bind-mounts local.db at /app/local.db, so SQLite data persists across container recreation; initialize it with pnpm run db:push before the first start. Provide production database and Better Auth values through apps/server/.env, and build the web image with the public API URL that browsers will use.
Continuous integration
Forgejo workflows run linting, formatting checks, focused type checks, and builds for the web/server applications and the portfolio. On pushes to main and version tags, they build and publish mosaic-web, mosaic-server, and mosaic-portfolio images to the configured Forgejo registry. The portfolio image is built from apps/portfolio/Dockerfile for linux/amd64 and is tagged with the short commit SHA, latest on main, and the version tag for releases.