Skip to main content
This guide describes how to set up your local development environment for contributing to Pangolin. We recommend using Docker Compose for the most consistent development experience across different environments.

Prerequisites

  • NodeJS v20.10.0
  • NPM v10.2.3 (or similar)
  • Go v1.23.1
  • Git
  • Docker & Docker Compose
For managing multiple versions of Go, you may want to use gvm. For managing multiple versions of NodeJS, you may want to use nvm.

Setup Your Repository

Below is an example if you’re working on the Pangolin repository.
1

Fork and clone

Fork the repository(ies) to your own GitHub account and clone to your local device:
git clone https://github.com/YOUR_USERNAME/pangolin.git
cd pangolin/
2

Add upstream remote

Add the remote upstream:
git remote add upstream https://github.com/fosrl/pangolin.git
3

Create feature branch

Create a new branch:
git checkout -b BRANCH_NAME dev
It is recommended to give your branch a meaningful name, relevant to the feature or fix you are working on.Good examples:
  • docs-docker
  • feature-new-system
  • fix-title-cards
Bad examples:
  • bug
  • docs
  • feature
  • fix
  • patch
4

Open pull request

If you open a pull request, open it against the dev branch of the original repository.

Important Best Practices for PRs

  • Keep PRs small and single-purpose: One feature, fix, or improvement per PR for easier review and testing.
  • Prefer improvements over new features: If you want to propose a net-new feature, contact us by email or on Discord first so we can confirm it fits the roadmap and help scope it.
  • Frontend consistency:
    • Use existing styles, components, and patterns.
    • Use Credenza for modals and Zod for form validation.
    • Keep Tailwind classes minimal; prefer component defaults.
    • Look for an existing example and mirror that pattern. Extract a small reusable component only when it clearly improves reuse.
  • Stick to established patterns: Avoid introducing new architectures or abstractions without discussing them with us first.
  • Auth changes require extra care:
    • Pangolin is multi-tenant. Handle user controls at the org level (varies by control) or globally via the server admin panel as appropriate.
    • Protect all API routes with the correct middleware and verify user permissions and access to referenced entities before performing actions.
  • Database changes:
    • Keep SQLite and Postgres schemas fully in sync and backward compatible.
    • Use datatypes supported by both databases.
    • No need to write versioned migrations; maintainers will handle these during releases.
  • Add visuals: Include screenshots or short videos when applicable to speed up reviews.

Databases

Pangolin supports two database types: SQLite and Postgres. You can switch between them with the provided scripts: Before running these, read local development setup below.
npm run set:sqlite
# or
npm run set:pg
After switching, regenerate and apply the schema using the matching scripts for that database. Keep both SQLite and Postgres schemas fully in sync and backward compatible.

Private Files and Directories

Pangolin includes both AGPLv3 code and some proprietary code licensed under the Fossorial Commercial License. Proprietary files include a license header and often live in directories whose names start with private. You may edit proprietary files in your PR as long as your PR includes the required CLA.
  • Frontend: no proprietary code.
  • Backend: proprietary code exists, primarily under server/private/. Subdirectories mirror the structure under server/.
To keep the AGPLv3 distribution fully compliant, be careful about imports:
  • AGPLv3 files must never import from the private directory. In TypeScript, the alias #private/ points to proprietary code and should only be used inside other private files.
  • If you must expose proprietary behavior to AGPLv3 code, use a dynamic import pattern. Create a file that mirrors the proprietary file’s relative location between server/private and server, and ensure the exported APIs have exactly matching function signatures. Dynamic import aliases start with #dynamic.
  • At build time, depending on the build flag, #dynamic imports are resolved to the appropriate implementation (AGPLv3 or proprietary).
Build flags control which distribution you are working on: oss, enterprise, or saas. Enterprise and SaaS include proprietary code; OSS must be 100% AGPLv3 compliant and excludes proprietary code. Use the existing npm scripts to switch:
npm run set:oss
# or npm run set:enterprise
# or npm run set:saas
Switching distributions updates TypeScript path aliases so #dynamic resolves to the correct locations. The build flag is also used in code to conditionally enable or disable features per distribution. As a rule of thumb, write as much AGPLv3 code as possible. Place only core, distribution-specific functionality in the proprietary layer (Enterprise/SaaS). Database schemas are never proprietary; all distributions share the same schemas. If you have any questions about this setup, email us or reach out on Discord.

Pangolin Development Setup

Choose your preferred development approach. We strongly recommend Docker Compose for the most consistent experience across all platforms.

Local Development

1

Install dependencies

Install package dependencies:
npm install
2

Configure environment

Ensure you have a config/ directory at the root with a config.yml inside. Refer to the Pangolin Configuration docs or the config.example.yml in the repo for a sample of what to include in that file.
You may need to tweak this to run in dev, such as setting the dashboard_url to http://localhost:3002.
3

Set your environment

Choose to build from the oss/enterprise/saas codebase:
npm run set:oss
# or npm run set:enterprise
# or npm run set:saas
Then choose your database:
npm run set:sqlite
# or npm run set:pg
4

Generate database schema

Generate the database schema and push it:
npm run db:sqlite:generate
npm run db:sqlite:push
5

Start development server

Start the development server using Docker Compose:
docker compose up --build
Or, start the development server directly:
npm run dev

Exit Nodes

When running Pangolin for the first time there will be no exit nodes. This means that there have been no Gerbil “exit nodes” registered in the database, and therefore, you cannot create Newt sites. When Gerbil first starts up and requests its config from Pangolin for the first time it gets registered as an exit node. The easiest way to resolve this is to run Gerbil and have it register in your dev environment. Download the Gerbil binary and run it with localhost:
./gerbil \
--reachableAt=http://localhost:3004 \
--generateAndSaveKeyTo=/var/config/key \
--remoteConfig=http://localhost:3001/api/v1/
Or enter in a dummy exit-node manually to the database.

Windows Development Considerations

Windows users with Docker Desktop + WSL2: File change detection may not work properly when project files are stored on the Windows filesystem.

Component Development

Gerbil

  • Go v1.23.1
make local

Newt

  • Go v1.23.1
make local

Olm

  • Go v1.23.1
make local