A mosaic of personal applications—distinct tools forming one connected suite. https://faceguy.dev
  • TypeScript 96.3%
  • CSS 2.7%
  • Dockerfile 0.9%
  • JavaScript 0.1%
Find a file
renovatebot c385187af4
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
chore(deps): pin node.js
2026-09-08 20:04:17 +03:00
.agents/skills docs(skills): push release tags by default 2026-08-22 01:27:15 +03:00
.forgejo/workflows fix(deps): pin dependencies 2026-09-07 20:01:45 +00:00
.zed initial commit 2026-08-02 14:16:46 +03:00
apps chore(deps): pin node.js 2026-09-08 20:04:17 +03:00
packages fix(deps): pin dependencies 2026-09-07 20:01:45 +00:00
.dockerignore initial commit 2026-08-02 14:16:46 +03:00
.gitattributes initial commit 2026-08-02 14:16:46 +03:00
.gitignore initial commit 2026-08-02 14:16:46 +03:00
.oxfmtrc.json chore: add ci/cd pipeline 2026-08-02 15:26:00 +00:00
.oxlintrc.json initial commit 2026-08-02 14:16:46 +03:00
AGENTS.md docs(agents): tighten repository guidance 2026-08-22 01:16:14 +03:00
bts.jsonc initial commit 2026-08-02 14:16:46 +03:00
docker-compose.yml feat(docker): add portfolio service 2026-08-21 12:10:19 +03:00
package.json fix(deps): pin dependencies 2026-09-07 20:01:45 +00:00
pnpm-lock.yaml fix(deps): pin dependencies 2026-09-07 20:01:45 +00:00
pnpm-workspace.yaml fix(deps): pin dependencies 2026-09-07 20:01:45 +00:00
README.md docs(readme): clarify setup and deployment 2026-08-22 01:16:17 +03:00
renovate.json chore(renovate): switch assignees to reviewers 2026-08-04 23:09:01 +03:00
skills-lock.json initial commit 2026-08-02 14:16:46 +03:00
tsconfig.json initial commit 2026-08-02 14:16:46 +03:00
turbo.json feat(web): configure server URL at runtime (#9) 2026-08-04 18:57:13 +00:00

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

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.