Skip to content

Latest commit

 

History

286 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WolfSlip

An open-source, self-hosted invoice & payslip generator.

WolfSlip produces invoices and payslips on infrastructure you control. There is no hosted tier and no third party in the middle - you run the app, you own the Postgres database, and your billing and payroll records never leave your environment.

WolfSlip is not tied to any one country. Currency, locale, tax-identifier label, payment details and the fiscal-year boundary are all configured from Settings. The design canvas uses an Indian business as its worked example, and those values are samples, nothing more.

  • License: MIT - see LICENSE
  • Status: early development, built in phases. Invoices work end to end except for PDF rendering and email delivery, which are next.

Status

Area State
Monorepo, CI, local Supabase stack Working
Authentication (email + password) Working
Design system and application shell Working
Settings (company, numbering, recipients, SMTP status) Working
Clients, employees, PDF templates Working
Invoices - draft, issue, void, numbering Working
PDF rendering Not started
Email delivery Not started
Payslips and pay runs Not started

Signup is deliberately closed. Every authenticated user can read and write the whole ledger on a single-org deployment, so accounts are created by an operator


Documentation

Document What it covers
docs/ARCHITECTURE.md How data is fetched, written and secured; the layer map
docs/DESIGN_SYSTEM.md Tokens, globals.css, and how to add UI without breaking the theme
docs/DATABASE.md Schema layout, RLS conventions, document numbering, migrations
docs/DECISIONS.md Decisions taken so far and why, including the rejected alternatives
docs/CONTRIBUTING.md Commands, scripts, conventions, the verification gate
AGENTS.md Rules an automated contributor must follow
TROUBLESHOOTING.md Failures that have actually happened here, and their causes

Stack

Concern Choice
Framework Next.js 16 (App Router, Cache Components)
Language TypeScript
Database & Auth Supabase (Postgres + RLS + GoTrue)
Auth SSR @supabase/ssr
Server Actions next-safe-action + Zod
Client data fetching TanStack Query
UI shadcn/ui on Radix UI
Styling Tailwind CSS v4, tokens in globals.css
Forms React Hook Form + Zod resolvers
PDF (planned) @pdfme/generator, Node runtime
Email (planned) nodemailer over SMTP
Lint / Format oxlint + oxfmt
Unit tests Vitest
Database tests pgTAP
E2E tests Playwright
Monorepo Turborepo + pnpm workspaces

Quick start

Requirements: Node 24+, pnpm 11+, Docker (for the local Supabase stack).

./setup.sh
pnpm dev

setup.sh installs dependencies, creates the .env files from their examples, starts Supabase, syncs the local keys into your env, creates the storage buckets, and generates database types.

The app runs at http://localhost:3000, Supabase Studio at http://localhost:54323, and the local mail catcher at http://localhost:54324.

Sign in with the account seeded into the local database:

staff@wolfslip.test / Wolf-Test-1!

That account is created by seed.sql, which runs only against the local stack - supabase db push applies migrations, never seeds - so it cannot reach a deployed database.


Layout

wolfslip/
├── apps/
│   ├── web/                            # Next.js application
│   │   └── src/
│   │       ├── app/
│   │       │   ├── (marketing)/        # Public pages
│   │       │   ├── (auth)/             # Sign in, reset, callback routes
│   │       │   └── (app)/              # Authenticated app
│   │       ├── components/ui/          # shadcn/ui - edited source, not a dependency
│   │       ├── data/                   # Server Actions (every write)
│   │       ├── lib/
│   │       │   ├── db/                 # Select strings, row -> view-model mappers
│   │       │   ├── queries/            # Isomorphic reads; loaders.ts is server-only
│   │       │   ├── query/              # TanStack client and provider
│   │       │   ├── schemas/            # Zod schemas, shared by forms and actions
│   │       │   ├── format/             # Money, amount-in-words, periods, numbering
│   │       │   ├── supabase/           # client / server / route / admin / proxy
│   │       │   └── types.ts            # View models - the only types components import
│   │       └── styles/globals.css      # The design system
│   └── database/
│       └── supabase/
│           ├── schemas/                # Declarative schema (source of truth)
│           ├── migrations/             # Generated - never hand-edited
│           ├── tests/                  # pgTAP
│           └── seed.sql                # Local fixtures and the dev account
├── docs/                               # Architecture, design system, decisions
├── packages/typescript-config/         # Shared tsconfig base
└── scripts/                            # Maintenance CLIs, run through tsx

Commands

Run from the repo root.

Command Does
pnpm dev Run every app in parallel
pnpm web#dev Run only the Next.js app
pnpm build Production build
pnpm lint oxlint over apps/web/src and scripts/
pnpm typecheck tsc --noEmit for the app and for scripts/
pnpm test Vitest unit tests
pnpm test-db pgTAP tests against the local stack
pnpm test:e2e Playwright end-to-end tests
pnpm database#start / #stop / #status Local Supabase stack
pnpm gen-types-local Regenerate database.types.ts from the local DB
pnpm gen-types Regenerate types from the linked remote project
pnpm supabase:sync-env Copy local Supabase keys into your env files
pnpm user:create <email> [password] Create a staff account
pnpm storage:bootstrap Create the storage buckets (they are DML, so migrations cannot)
pnpm shadcn:add <component> Add a shadcn component without reverting this project's tokens

Never run shadcn add directly - see docs/DESIGN_SYSTEM.md.


Changing the database

The declarative schema under apps/database/supabase/schemas/ is the source of truth, split by concern and applied in the order declared under [db.migrations] schema_paths in config.toml. Never hand-write a migration.

# 1. Edit the relevant file in apps/database/supabase/schemas/
# 2. Generate the migration
cd apps/database && pnpm supabase db diff -f add_payslips
# 3. Apply locally, regenerate types, re-run the database tests
pnpm supabase db reset
cd ../.. && pnpm gen-types-local && pnpm test-db

Every table needs Row Level Security policies and explicit GRANTs - RLS alone makes a table unreachable through PostgREST, and a grant without RLS is a leak. New tables also inherit a GRANT ALL … TO anon default privilege from the Supabase platform baseline, which the migration must revoke. pnpm test-db asserts all of this generically, so a table that forgets fails there rather than in production. Full detail in docs/DATABASE.md.


Creating accounts

Public signup is disabled (enable_signup = false in apps/database/supabase/config.toml) because every authenticated user can read the whole ledger. Create accounts deliberately:

pnpm user:create finance@example.com          # generates and prints a password
pnpm user:create finance@example.com 'secret' # or set one

This calls the Auth Admin API with the service-role key, which bypasses the signup gate. It needs SUPABASE_SERVICE_ROLE_KEY in the environment.


Self-hosting

WolfSlip is a Next.js app plus a Postgres database, so it runs anywhere Node 24+ runs. You need:

  1. A Postgres database with the Supabase extensions - from supabase.com or a self-hosted Supabase.
  2. The migrations in apps/database/supabase/migrations applied to it.
  3. The storage buckets created (pnpm storage:bootstrap) - bucket creation is DML, so migrations cannot do it.
  4. These environment variables on the web app:
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=
NEXT_PUBLIC_SITE_URL=
SUPABASE_SERVICE_ROLE_KEY=      # server-only; never prefix this NEXT_PUBLIC_

Email delivery, once built, adds SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SMTP_FROM and SMTP_REQUIRE_TLS. The SMTP password lives in the environment and never in Postgres, so a database dump carries no mail credentials.

Then pnpm build && pnpm --filter web start.


Contributing

Before opening a PR:

pnpm lint && pnpm typecheck && pnpm test && pnpm test-db && pnpm build

See docs/CONTRIBUTING.md for conventions, and docs/DECISIONS.md before proposing a change to an architectural choice - the reasoning, including what was rejected, is recorded there.


License

MIT. See LICENSE.

About

WolfSlip is an open-source, self-hosted invoice generator that automates recurring payslips and centralizes contractor billing

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages