Files
2026-11-09 16:23:48 -05:00

8.1 KiB

Repo map

A quick map of where things live, so you can go straight to the right file. Path aliases (from tsconfig*.json):

Alias Resolves to Use from
@server/* server/* anywhere (server and UI, for shared types)
@app/*, @/* src/* UI
@test/*, @cli/* test/*, cli/* tests, CLI
#private/* server/private/* only inside server/private/
#dynamic/* server/private/* (enterprise/saas) or server/* (oss) outside server/private/ when you need the private implementation on paid builds
#open/*, #closed/* server/*, server/private/* internal to the esbuild #dynamic switcher; don't use

Top level

server/          Express API server (TypeScript, bundled with esbuild.mjs)
src/             Next.js dashboard (App Router)
messages/        i18n JSON. Only en-US.json is edited by hand; Crowdin generates the others
config/          config.yml (+ privateConfig.yml on enterprise), example configs
cli/             pangctl CLI
install/         installer (Go)
docs/            Developer docs for agents and contributors (this folder)
test/            Shared test helpers
scripts/         Build/test scripts (run-tests.mjs)
esbuild.mjs      Server bundler; holds the #private guard and the #dynamic switcher
drizzle.*.ts     Drizzle configs (drizzle.config.ts is generated by set:pg / set:sqlite)

Generated and gitignored, switched by npm scripts: server/build.ts (npm run set:oss|enterprise|saas), tsconfig.json (copied from tsconfig.{oss,enterprise,saas}.json), server/db/index.ts, drizzle.config.ts, server/setup/migrations.ts (npm run set:sqlite|pg).

server/

server/
  index.ts                 Entry. Boots config, migrations, and all servers below
  apiServer.ts             External API (dashboard + user API). Routes: #dynamic/routers/external
  internalServer.ts        Internal API (Next server-side fetches, badger). Routes: #dynamic/routers/internal
  integrationApiServer.ts  Integration API (API keys, OpenAPI docs). Routes: #dynamic/routers/integration
  aiGatewayServer.ts       AI gateway
  nextServer.ts            Serves the Next app
  openApi.ts               OpenAPITags enum + registry
  build.ts                 GENERATED: `build` = "oss" | "enterprise" | "saas"
  auth/                    actions.ts (ActionsEnum), sessions/, password, totp, canUserAccess*.ts
  middlewares/             verify*Access.ts, verifyUserHasAction, verifyLimits, logActionAudit (stub)
    integration/           verifyApiKey*.ts counterparts for the integration API
  routers/                 One folder per entity (see below) + router files:
    external.ts            Dashboard/user routes (session auth)
    internal.ts            Internal routes
    integration.ts         API-key routes (needs public-/private-resource aliases)
    hybrid.ts              Hybrid/remote node routes
  private/                 ENTERPRISE/SAAS ONLY (see docs/enterprise-private-code.md)
    routers/{external,internal,integration,hybrid}.ts   Import the public routers and add private routes
    routers/<entity>/      Private-only handlers
    lib/                   Private implementations of #dynamic modules, config, redis, stripe, billing
    lib/readConfigFile.ts  privateConfigSchema (private config values)
    lib/config.ts          `privateConfig` singleton
    middlewares/           Real logActionAudit etc.
  lib/
    config.ts              `config` singleton: config.getRawConfig()
    readConfigFile.ts      Public config zod schema
    crypto.ts              encrypt/decrypt (AES, keyed by server.secret)
    blueprints/            Blueprint schema (types.ts) + apply (publicResources.ts, privateResources.ts) + export
    traefik/               Traefik dynamic config generation for public resources
    billing/tierMatrix.ts  TierFeature -> tiers; used by server and UI paywalls
    response.ts            `response()` envelope helper
    validators.ts, schemas.ts  Shared zod validators
    deleteResource.ts, deleteSiteResource.ts, deleteOrg.ts   Cascading delete helpers
    rebuildClientAssociations.ts   Recomputes client <-> site resource access
  db/
    index.ts               GENERATED: re-exports ./pg or ./sqlite + `driver`
    pg/schema/{schema,privateSchema}.ts       Postgres tables
    sqlite/schema/{schema,privateSchema}.ts   SQLite tables (must mirror pg)
  setup/                   Startup tasks + migrations (scriptsPg/, scriptsSqlite/). Don't edit unless asked
  emails/                  React email templates
  routers/newt/            Messages to/from Newt (site connector) over websockets
  routers/olm/             Messages to/from Olm (client connector)
  routers/gerbil/          Exit node (WireGuard) coordination
  routers/badger/          Traefik auth plugin endpoints (verifySession)
  routers/traefik/         Traefik HTTP config provider
  routers/ws/              Websocket server (private/routers/ws for enterprise)

Entity routers (server/routers/<entity>/)

Each folder has index.ts (re-exports), one file per operation (createX.ts, getX.ts, listXs.ts, updateX.ts, deleteX.ts), and often types.ts / validation.ts. See docs/crud-endpoints.md.

Main ones: org, site, resource (public resources), siteResource (private resources), target (public resource targets), policy (resource policies), client, user, role, domain, idp, apiKeys, accessToken, blueprints, labels, redirect, aiProvider, aiBudget, virtualApiKey, auditLogs, certificates.

src/ (dashboard)

src/
  app/
    layout.tsx, navigation.tsx   Root layout; sidebar nav items (orgNavSections etc.)
    [orgId]/settings/            All org settings pages (one folder per section)
      layout.tsx                 Sidebar shell
      general/                   Org settings: layout.tsx (tabs) + page.tsx, security/, logs/, auth-page/
      resources/public/          Public resources: page.tsx (list), create/, [niceId]/<tab>/
      resources/private/         Private (site) resources: page.tsx, create/, [niceId]/<tab>/
      sites/, clients/, access/, domains/, api-keys/, blueprints/, ai-providers/, ...
      (private)/                 Route group for enterprise-only pages (billing, idp, license, policies, remote-exit-nodes)
    admin/                       Server admin pages
    auth/                        Login, signup, 2FA, resource auth pages
  components/
    ui/                          shadcn primitives (button, form, input, select, tabs, ...)
    Credenza.tsx                 Responsive modal (dialog on desktop, drawer on mobile). Use for every modal
    DismissableBanner.tsx        Help/info banner (wrapped by *Banner.tsx components)
    HorizontalTabs.tsx           Route-based tabs used in [id]/layout.tsx files
    Settings.tsx                 SettingsContainer / SettingsSection / ... layout primitives
    SettingsSectionTitle.tsx     Page title + description
    PaidFeaturesAlert.tsx        Paywall notice for tiered features
    ConfirmDeleteDialog.tsx      Type-the-name-to-delete Credenza
  contexts/ + providers/ + hooks/  Context per entity: OrgProvider/useOrgContext,
                                   ResourceProvider/useResourceContext,
                                   SiteResourceProvider/useSiteResourceContext, useEnvContext, usePaidStatus, ...
  lib/
    api/                         index.ts: createApiClient (client), `internal` (server components), formatAxiosError; cookies.ts: authCookieHeader
    pullEnv.ts                   Builds the Env object (flags etc.) from process.env for the UI

Fetching data

  • Server components / layouts: internal.get<AxiosResponse<T>>(path, await authCookieHeader()), with internal from @app/lib/api and authCookieHeader from @app/lib/api/cookies. Use getCachedOrg / getCachedOrgUser from @app/lib/api/* for the org.
  • Client components: const api = createApiClient(useEnvContext());, then api.get/put/post/delete. Errors go to toast({ variant: "destructive", description: formatAxiosError(e, t("...")) }).
  • Response types are imported straight from the server router, e.g. import { GetResourceResponse } from "@server/routers/resource".