- TypeScript 96.3%
- CSS 2.7%
- Dockerfile 0.9%
- JavaScript 0.1%
|
All checks were successful
Portfolio CI / lint-and-build (pull_request) Successful in 25s
Server CI / lint-and-build (pull_request) Successful in 22s
Web CI / lint-and-build (pull_request) Successful in 36s
Portfolio CI / build-and-publish (pull_request) Successful in 32s
Server CI / build-and-publish (pull_request) Successful in 57s
Web CI / build-and-publish (pull_request) Successful in 38s
Portfolio CI / lint-and-build (push) Successful in 27s
Server CI / lint-and-build (push) Successful in 23s
Web CI / lint-and-build (push) Successful in 36s
Portfolio CI / build-and-publish (push) Successful in 32s
Server CI / build-and-publish (push) Successful in 57s
Web CI / build-and-publish (push) Successful in 38s
|
||
|---|---|---|
| .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 start page for bookmarks. Sign in, group links into spaces and categories, mark favorites, and search saved links or the web. This repository also contains a separate portfolio site that shares the UI package.
Stack
- Next.js 16 and React 19 for the web and portfolio apps
- Hono, oRPC, and OpenAPI for the API
- Better Auth for email and password sign-in
- Drizzle with SQLite or Turso/libSQL
- TypeScript, pnpm 10, Turborepo, Oxlint, and Oxfmt
Requirements
- Node.js 24
- pnpm 10.34.5, preferably through Corepack
- A local or remote SQLite/libSQL database
Run locally
Install dependencies from the repository root:
corepack enable
pnpm install
Create apps/server/.env:
# `pnpm run db:local` starts a local database on port 8080.
# A Turso/libSQL URL also works here.
DATABASE_URL=http://127.0.0.1:8080
# Use a unique value 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
# Use true or 1 to disable new-account registration.
DISABLE_REGISTRATION=false
BETTER_AUTH_URL, CORS_ORIGIN, and SERVER_URL must be the browser-facing URLs. Use HTTPS in production because Better Auth stores sessions in secure cookies. The web app validates SERVER_URL on the server, then passes it to the browser for API requests. In Docker, it must be reachable from both the web container and a user's browser.
Start a local database in one terminal, then apply the schema:
pnpm run db:local
pnpm run db:push
For Turso or another remote libSQL database, set DATABASE_URL and run pnpm run db:push. For managed schema changes, generate and apply a migration instead:
pnpm run db:generate
pnpm run db:migrate
Start all development apps:
pnpm run dev
- Web app: http://localhost:3001
- API: http://localhost:3000
- OpenAPI reference: http://localhost:3000/api-reference
- Portfolio: http://localhost:3002
Run a single app when you do not need the rest:
pnpm run dev:web
pnpm run dev:server
pnpm --filter portfolio dev
What the start page does
- Organizes bookmarks into spaces and categories
- Favorites links and searches saved bookmarks
- Sends web searches to a configurable search engine
- Stores a per-user search query pattern
- Supports email and password sign-in plus light and dark themes
Repository layout
apps/
├── web/ Authenticated start page
├── portfolio/ Standalone portfolio site
└── server/ Hono server, Better Auth, oRPC, and OpenAPI
packages/
├── api/ oRPC routers and application logic
├── auth/ Better Auth configuration
├── config/ Shared TypeScript configuration
├── db/ Drizzle schema, migrations, and database client
├── env/ Server and web environment validation
└── ui/ Shared shadcn/ui components, hooks, and styles
Everyday commands
| Command | Purpose |
|---|---|
pnpm run build |
Build every workspace. |
pnpm run check-types |
Type-check every workspace. |
pnpm run check |
Run Oxlint and write Oxfmt formatting changes. |
pnpm run db:local |
Start the local Turso development database. |
pnpm run db:push |
Apply the current Drizzle schema. |
pnpm run db:generate |
Generate a Drizzle migration. |
pnpm run db:migrate |
Apply generated migrations. |
pnpm run db:studio |
Open Drizzle Studio. |
Shared UI
Shared components live in packages/ui. Import public exports rather than package internals:
import { Button } from "@mosaic/ui/components/basic/button";
Add shared shadcn primitives from the repository root:
pnpm dlx shadcn@latest add accordion dialog popover sheet table -c packages/ui
Run the shadcn CLI from apps/web or apps/portfolio for an app-specific component.
Docker Compose
Compose builds and runs the web app, API, and portfolio site:
pnpm run docker:build
pnpm run docker:up
pnpm run docker:logs
pnpm run docker:down
The services listen on ports 3001, 3000, and 3002. Compose optionally reads apps/web/.env, apps/server/.env, and apps/portfolio/.env. The server mounts local.db at /app/local.db, so SQLite data survives container recreation. Initialize it with pnpm run db:push before the first start. Build the web image with the public API URL and provide production database and Better Auth values through apps/server/.env.
CI
Forgejo checks linting, formatting, focused type checks, and builds for the web, server, and portfolio apps. Pushes to main and version tags publish mosaic-web, mosaic-server, and mosaic-portfolio images to the configured Forgejo registry. The portfolio image targets linux/amd64 and receives the short commit SHA, latest on main, and the release tag when applicable.