- Svelte 49.1%
- mdsvex 32.6%
- TypeScript 8%
- CSS 6.1%
- JavaScript 2.2%
- Other 2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .forgejo/workflows | ||
| .vscode | ||
| scripts | ||
| src | ||
| static | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| .mcp.json | ||
| .npmrc | ||
| .nvmrc | ||
| .prettierignore | ||
| .prettierrc | ||
| CLAUDE.md | ||
| compose.yaml | ||
| Dockerfile | ||
| eslint.config.js | ||
| LICENSE | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| svelte.config.js | ||
| tsconfig.json | ||
| vite.config.ts | ||
rollnote-website
The rollnote marketing site and user documentation (roll-note.com), built with
SvelteKit 2 / Svelte 5, Tailwind CSS 4 and mdsvex (docs pages are .svx).
@sveltejs/adapter-node produces a small Node server: almost every page is
prerendered at build time, /changelog is rendered per request from the
rollnote-api changelog, and /health answers the container healthcheck.
Prerequisites
- Node.js 24+ (
.nvmrc;enginesis enforced through.npmrc) - pnpm, the version pinned by
packageManager(corepack enable)
Setup
pnpm install
cp .env.example .env # public URLs, see below
pnpm dev # http://localhost:5173
Environment
All three variables are public and inlined at build time through
$env/static/public (src/lib/config.ts), so they must be set when building:
| Variable | Production value | Used for |
|---|---|---|
PUBLIC_SITE_URL |
https://roll-note.com |
canonical URLs, sitemap, JSON-LD |
PUBLIC_APP_URL |
https://app.roll-note.com |
every "open the app" / signup CTA |
PUBLIC_API_URL |
https://api.roll-note.com |
/changelog (release notes) |
Scripts
pnpm dev # Vite dev server
pnpm build # production build -> build/ (run with `node build`)
pnpm preview # preview the production build
pnpm check # svelte-kit sync + svelte-check
pnpm lint # prettier --check . + eslint
pnpm format # prettier --write .
pnpm api:types # regenerate src/lib/api/schema.d.ts from ../rollnote-api/openapi.json
Deployment
Dockerfile builds the site with Node 24 (alpine) and ships only the
adapter-node output (build/ + package.json, no node_modules). The server
runs as the node user on port 3000 and exposes GET /health.
The public URLs are build args with the production values as defaults
(.env never enters the build context). compose.yaml forwards
PUBLIC_SITE_URL, PUBLIC_APP_URL and PUBLIC_API_URL from the shell or a
compose .env, so a self-hosted build only needs those set:
docker compose up -d --build
# or: docker build --build-arg PUBLIC_SITE_URL=https://www.example.org ... .
The container runs read-only (tmpfs /tmp), with no-new-privileges, all
capabilities dropped, CPU/memory/pid limits and log rotation, on the external
websites network.
Security headers (CSP, HSTS, X-Frame-Options, Referrer-Policy,
Permissions-Policy) are set by the reverse proxy, not by this server:
prerendered pages are served straight from disk by adapter-node before any
SvelteKit hook runs, so a handle hook could never add them.
CI
.forgejo/workflows/test.yml runs when the run-ci label is added to a PR:
lint, svelte-check, a drift check of the generated API types against
rollnote-api's openapi.json, the production build, a gitleaks secret scan and a
Docker image build (not pushed).