Full Code of fosrl/pangolin for AI

main 484326853715 cached
1253 files
8.9 MB
2.4M tokens
2674 symbols
1 requests
Download .txt
Showing preview only (9,558K chars total). Download the full file or copy to clipboard to get everything.
Repository: fosrl/pangolin
Branch: main
Commit: 484326853715
Files: 1253
Total size: 8.9 MB

Directory structure:
gitextract_nwjdm6gt/

├── .dockerignore
├── .editorconfig
├── .eslintrc.json
├── .github/
│   ├── DISCUSSION_TEMPLATE/
│   │   └── feature-requests.yml
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── 1.bug_report.yml
│   │   └── config.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── cicd.yml
│       ├── linting.yml
│       ├── mirror.yaml
│       ├── restart-runners.yml
│       ├── saas.yml
│       ├── stale-bot.yml
│       └── test.yml
├── .gitignore
├── .nvmrc
├── .prettierignore
├── .prettierrc
├── .vscode/
│   ├── extensions.json
│   └── settings.json
├── CONTRIBUTING.md
├── Dockerfile
├── Dockerfile.dev
├── LICENSE
├── Makefile
├── README.md
├── SECURITY.md
├── bruno/
│   ├── API Keys/
│   │   ├── Create API Key.bru
│   │   ├── Delete API Key.bru
│   │   ├── List API Key Actions.bru
│   │   ├── List Org API Keys.bru
│   │   ├── List Root API Keys.bru
│   │   ├── Set API Key Actions.bru
│   │   ├── Set API Key Orgs.bru
│   │   └── folder.bru
│   ├── Auth/
│   │   ├── 2fa-disable.bru
│   │   ├── 2fa-enable.bru
│   │   ├── 2fa-request.bru
│   │   ├── change-password.bru
│   │   ├── login.bru
│   │   ├── logout.bru
│   │   ├── reset-password-request.bru
│   │   ├── reset-password.bru
│   │   ├── signup.bru
│   │   ├── verify-email-request.bru
│   │   ├── verify-email.bru
│   │   └── verify-user.bru
│   ├── Clients/
│   │   ├── createClient.bru
│   │   └── pickClientDefaults.bru
│   ├── IDP/
│   │   ├── Create OIDC Provider.bru
│   │   ├── Generate OIDC URL.bru
│   │   └── folder.bru
│   ├── Internal/
│   │   ├── Traefik Config.bru
│   │   └── folder.bru
│   ├── Newt/
│   │   ├── Create Newt.bru
│   │   └── Get Token.bru
│   ├── Olm/
│   │   ├── createOlm.bru
│   │   └── folder.bru
│   ├── Orgs/
│   │   ├── Check Id.bru
│   │   └── listOrgs.bru
│   ├── Remote Exit Node/
│   │   └── createRemoteExitNode.bru
│   ├── Resources/
│   │   ├── listResourcesByOrg.bru
│   │   └── listResourcesBySite.bru
│   ├── Sites/
│   │   ├── Get Site.bru
│   │   └── listSites.bru
│   ├── Targets/
│   │   └── listTargets.bru
│   ├── Test.bru
│   ├── Traefik/
│   │   └── traefik-config.bru
│   ├── Users/
│   │   ├── adminListUsers.bru
│   │   ├── adminRemoveUser.bru
│   │   └── getUser.bru
│   └── bruno.json
├── cli/
│   ├── commands/
│   │   ├── clearExitNodes.ts
│   │   ├── clearLicenseKeys.ts
│   │   ├── deleteClient.ts
│   │   ├── generateOrgCaKeys.ts
│   │   ├── resetUserSecurityKeys.ts
│   │   ├── rotateServerSecret.ts
│   │   └── setAdminCredentials.ts
│   ├── index.ts
│   └── wrapper.sh
├── components.json
├── config/
│   ├── .gitkeep
│   ├── config.example.yml
│   ├── db/
│   │   └── .gitkeep
│   ├── logs/
│   │   └── .gitkeep
│   └── traefik/
│       ├── dynamic_config.yml
│       └── traefik_config.yml
├── crowdin.yml
├── docker-compose.drizzle.yml
├── docker-compose.example.yml
├── docker-compose.pgr.yml
├── docker-compose.yml
├── drizzle.pg.config.ts
├── drizzle.sqlite.config.ts
├── esbuild.mjs
├── eslint.config.js
├── install/
│   ├── Makefile
│   ├── config/
│   │   ├── config.yml
│   │   ├── crowdsec/
│   │   │   ├── acquis.d/
│   │   │   │   ├── appsec.yaml
│   │   │   │   └── traefik.yaml
│   │   │   ├── docker-compose.yml
│   │   │   ├── dynamic_config.yml
│   │   │   ├── profiles.yaml
│   │   │   └── traefik_config.yml
│   │   ├── docker-compose.yml
│   │   └── traefik/
│   │       ├── dynamic_config.yml
│   │       └── traefik_config.yml
│   ├── config.go
│   ├── containers.go
│   ├── crowdsec.go
│   ├── get-installer.sh
│   ├── go.mod
│   ├── go.sum
│   ├── input.go
│   ├── input.txt
│   ├── main.go
│   └── theme.go
├── messages/
│   ├── bg-BG.json
│   ├── cs-CZ.json
│   ├── de-DE.json
│   ├── en-US.json
│   ├── es-ES.json
│   ├── fr-FR.json
│   ├── it-IT.json
│   ├── ko-KR.json
│   ├── nb-NO.json
│   ├── nl-NL.json
│   ├── pl-PL.json
│   ├── pt-PT.json
│   ├── ru-RU.json
│   ├── tr-TR.json
│   ├── zh-CN.json
│   └── zh-TW.json
├── next.config.ts
├── package.json
├── postcss.config.mjs
├── server/
│   ├── apiServer.ts
│   ├── auth/
│   │   ├── actions.ts
│   │   ├── canUserAccessResource.ts
│   │   ├── canUserAccessSiteResource.ts
│   │   ├── checkValidInvite.ts
│   │   ├── password.ts
│   │   ├── passwordSchema.ts
│   │   ├── resourceOtp.ts
│   │   ├── sendEmailVerificationCode.ts
│   │   ├── sessions/
│   │   │   ├── app.ts
│   │   │   ├── newt.ts
│   │   │   ├── olm.ts
│   │   │   ├── resource.ts
│   │   │   └── verifySession.ts
│   │   ├── totp.ts
│   │   ├── unauthorizedResponse.ts
│   │   └── verifyResourceAccessToken.ts
│   ├── cleanup.ts
│   ├── db/
│   │   ├── README.md
│   │   ├── asns.ts
│   │   ├── countries.ts
│   │   ├── ios_models.json
│   │   ├── mac_models.json
│   │   ├── maxmind.ts
│   │   ├── maxmindAsn.ts
│   │   ├── migrate.ts
│   │   ├── names.json
│   │   ├── names.ts
│   │   ├── pg/
│   │   │   ├── driver.ts
│   │   │   ├── index.ts
│   │   │   ├── logsDriver.ts
│   │   │   ├── migrate.ts
│   │   │   ├── safeRead.ts
│   │   │   └── schema/
│   │   │       ├── privateSchema.ts
│   │   │       └── schema.ts
│   │   ├── queries/
│   │   │   └── verifySessionQueries.ts
│   │   └── sqlite/
│   │       ├── driver.ts
│   │       ├── index.ts
│   │       ├── logsDriver.ts
│   │       ├── migrate.ts
│   │       ├── safeRead.ts
│   │       └── schema/
│   │           ├── privateSchema.ts
│   │           └── schema.ts
│   ├── emails/
│   │   ├── index.ts
│   │   ├── sendEmail.ts
│   │   └── templates/
│   │       ├── EnterpriseEditionKeyGenerated.tsx
│   │       ├── NotifyResetPassword.tsx
│   │       ├── NotifyUsageLimitApproaching.tsx
│   │       ├── NotifyUsageLimitReached.tsx
│   │       ├── ResetPasswordCode.tsx
│   │       ├── ResourceOTPCode.tsx
│   │       ├── SendInviteLink.tsx
│   │       ├── SupportEmail.tsx
│   │       ├── TwoFactorAuthNotification.tsx
│   │       ├── VerifyEmailCode.tsx
│   │       ├── WelcomeQuickStart.tsx
│   │       ├── components/
│   │       │   ├── ButtonLink.tsx
│   │       │   ├── CopyCodeBox.tsx
│   │       │   └── Email.tsx
│   │       └── lib/
│   │           └── theme.ts
│   ├── extendZod.ts
│   ├── index.ts
│   ├── integrationApiServer.ts
│   ├── internalServer.ts
│   ├── lib/
│   │   ├── asn.ts
│   │   ├── billing/
│   │   │   ├── createCustomer.ts
│   │   │   ├── features.ts
│   │   │   ├── getLineItems.ts
│   │   │   ├── getOrgTierData.ts
│   │   │   ├── index.ts
│   │   │   ├── licenses.ts
│   │   │   ├── limitSet.ts
│   │   │   ├── limitsService.ts
│   │   │   ├── tierMatrix.ts
│   │   │   └── usageService.ts
│   │   ├── blueprints/
│   │   │   ├── MaintenanceSchema.ts
│   │   │   ├── applyBlueprint.ts
│   │   │   ├── applyNewtDockerBlueprint.ts
│   │   │   ├── clientResources.ts
│   │   │   ├── parseDockerContainers.ts
│   │   │   ├── parseDotNotation.ts
│   │   │   ├── proxyResources.ts
│   │   │   └── types.ts
│   │   ├── cache.ts
│   │   ├── calculateUserClientsForOrgs.ts
│   │   ├── canUserAccessResource.ts
│   │   ├── certificates.ts
│   │   ├── checkOrgAccessPolicy.ts
│   │   ├── cleanupLogs.test.ts
│   │   ├── cleanupLogs.ts
│   │   ├── clientVersionChecks.ts
│   │   ├── colorsSchema.ts
│   │   ├── config.ts
│   │   ├── consts.ts
│   │   ├── corsWithLoginPage.ts
│   │   ├── crypto.ts
│   │   ├── deleteOrg.ts
│   │   ├── domainUtils.ts
│   │   ├── encryption.ts
│   │   ├── exitNodes/
│   │   │   ├── exitNodeComms.ts
│   │   │   ├── exitNodes.ts
│   │   │   ├── getCurrentExitNodeId.ts
│   │   │   ├── index.ts
│   │   │   └── subnet.ts
│   │   ├── geoip.ts
│   │   ├── getEnvOrYaml.ts
│   │   ├── hostMeta.ts
│   │   ├── idp/
│   │   │   └── generateRedirectUrl.ts
│   │   ├── ip.test.ts
│   │   ├── ip.ts
│   │   ├── isLicencedOrSubscribed.ts
│   │   ├── isSubscribed.ts
│   │   ├── lock.ts
│   │   ├── logAccessAudit.ts
│   │   ├── normalizePostAuthPath.ts
│   │   ├── rateLimitStore.ts
│   │   ├── readConfigFile.ts
│   │   ├── rebuildClientAssociations.ts
│   │   ├── response.ts
│   │   ├── s3.ts
│   │   ├── schemas.ts
│   │   ├── serverIpService.ts
│   │   ├── sshCA.ts
│   │   ├── stoi.ts
│   │   ├── telemetry.ts
│   │   ├── totp.ts
│   │   ├── traefik/
│   │   │   ├── TraefikConfigManager.ts
│   │   │   ├── getTraefikConfig.ts
│   │   │   ├── index.ts
│   │   │   ├── middleware.ts
│   │   │   ├── pathEncoding.test.ts
│   │   │   ├── traefikConfig.test.ts
│   │   │   └── utils.ts
│   │   ├── userOrg.ts
│   │   ├── validators.test.ts
│   │   └── validators.ts
│   ├── license/
│   │   └── license.ts
│   ├── logger.ts
│   ├── middlewares/
│   │   ├── csrfProtection.ts
│   │   ├── formatError.ts
│   │   ├── getUserOrgs.ts
│   │   ├── index.ts
│   │   ├── integration/
│   │   │   ├── index.ts
│   │   │   ├── verifyAccessTokenAccess.ts
│   │   │   ├── verifyApiKey.ts
│   │   │   ├── verifyApiKeyApiKeyAccess.ts
│   │   │   ├── verifyApiKeyClientAccess.ts
│   │   │   ├── verifyApiKeyDomainAccess.ts
│   │   │   ├── verifyApiKeyHasAction.ts
│   │   │   ├── verifyApiKeyIdpAccess.ts
│   │   │   ├── verifyApiKeyIsRoot.ts
│   │   │   ├── verifyApiKeyOrgAccess.ts
│   │   │   ├── verifyApiKeyResourceAccess.ts
│   │   │   ├── verifyApiKeyRoleAccess.ts
│   │   │   ├── verifyApiKeySetResourceClients.ts
│   │   │   ├── verifyApiKeySetResourceUsers.ts
│   │   │   ├── verifyApiKeySiteAccess.ts
│   │   │   ├── verifyApiKeySiteResourceAccess.ts
│   │   │   ├── verifyApiKeyTargetAccess.ts
│   │   │   └── verifyApiKeyUserAccess.ts
│   │   ├── logActionAudit.ts
│   │   ├── logIncoming.ts
│   │   ├── notFound.ts
│   │   ├── requestTimeout.ts
│   │   ├── stripDuplicateSessions.ts
│   │   ├── verifyAccessTokenAccess.ts
│   │   ├── verifyAdmin.ts
│   │   ├── verifyApiKeyAccess.ts
│   │   ├── verifyClientAccess.ts
│   │   ├── verifyDomainAccess.ts
│   │   ├── verifyIsLoggedInUser.ts
│   │   ├── verifyLimits.ts
│   │   ├── verifyOlmAccess.ts
│   │   ├── verifyOrgAccess.ts
│   │   ├── verifyResourceAccess.ts
│   │   ├── verifyRoleAccess.ts
│   │   ├── verifySession.ts
│   │   ├── verifySetResourceClients.ts
│   │   ├── verifySetResourceUsers.ts
│   │   ├── verifySiteAccess.ts
│   │   ├── verifySiteResourceAccess.ts
│   │   ├── verifyTargetAccess.ts
│   │   ├── verifyUser.ts
│   │   ├── verifyUserAccess.ts
│   │   ├── verifyUserHasAction.ts
│   │   ├── verifyUserInRole.ts
│   │   ├── verifyUserIsOrgOwner.ts
│   │   └── verifyUserIsServerAdmin.ts
│   ├── nextServer.ts
│   ├── openApi.ts
│   ├── private/
│   │   ├── auth/
│   │   │   └── sessions/
│   │   │       └── remoteExitNode.ts
│   │   ├── cleanup.ts
│   │   ├── lib/
│   │   │   ├── billing/
│   │   │   │   ├── createCustomer.ts
│   │   │   │   ├── getOrgTierData.ts
│   │   │   │   └── index.ts
│   │   │   ├── blueprints/
│   │   │   │   └── MaintenanceSchema.ts
│   │   │   ├── cache.ts
│   │   │   ├── certificates.ts
│   │   │   ├── checkOrgAccessPolicy.ts
│   │   │   ├── config.ts
│   │   │   ├── exitNodes/
│   │   │   │   ├── exitNodeComms.ts
│   │   │   │   ├── exitNodes.ts
│   │   │   │   └── index.ts
│   │   │   ├── isLicencedOrSubscribed.ts
│   │   │   ├── isSubscribed.ts
│   │   │   ├── lock.ts
│   │   │   ├── logAccessAudit.ts
│   │   │   ├── rateLimit.test.ts
│   │   │   ├── rateLimit.ts
│   │   │   ├── rateLimitStore.ts
│   │   │   ├── readConfigFile.ts
│   │   │   ├── redis.ts
│   │   │   ├── redisStore.ts
│   │   │   ├── stripe.ts
│   │   │   └── traefik/
│   │   │       ├── getTraefikConfig.ts
│   │   │       └── index.ts
│   │   ├── license/
│   │   │   ├── license.ts
│   │   │   └── licenseJwt.ts
│   │   ├── middlewares/
│   │   │   ├── index.ts
│   │   │   ├── logActionAudit.ts
│   │   │   ├── verifyCertificateAccess.ts
│   │   │   ├── verifyIdpAccess.ts
│   │   │   ├── verifyLoginPageAccess.ts
│   │   │   ├── verifyRemoteExitNode.ts
│   │   │   ├── verifyRemoteExitNodeAccess.ts
│   │   │   ├── verifySubscription.ts
│   │   │   └── verifyValidLicense.ts
│   │   └── routers/
│   │       ├── approvals/
│   │       │   ├── countApprovals.ts
│   │       │   ├── index.ts
│   │       │   ├── listApprovals.ts
│   │       │   └── processPendingApproval.ts
│   │       ├── auditLogs/
│   │       │   ├── exportAccessAuditLog.ts
│   │       │   ├── exportActionAuditLog.ts
│   │       │   ├── index.ts
│   │       │   ├── queryAccessAuditLog.ts
│   │       │   └── queryActionAuditLog.ts
│   │       ├── auth/
│   │       │   ├── getSessionTransferToken.ts
│   │       │   ├── index.ts
│   │       │   └── transferSession.ts
│   │       ├── billing/
│   │       │   ├── changeTier.ts
│   │       │   ├── createCheckoutSession.ts
│   │       │   ├── createPortalSession.ts
│   │       │   ├── featureLifecycle.ts
│   │       │   ├── getOrgSubscriptions.ts
│   │       │   ├── getOrgUsage.ts
│   │       │   ├── hooks/
│   │       │   │   ├── getSubType.ts
│   │       │   │   ├── handleCustomerCreated.ts
│   │       │   │   ├── handleCustomerDeleted.ts
│   │       │   │   ├── handleCustomerUpdated.ts
│   │       │   │   ├── handleSubscriptionCreated.ts
│   │       │   │   ├── handleSubscriptionDeleted.ts
│   │       │   │   └── handleSubscriptionUpdated.ts
│   │       │   ├── index.ts
│   │       │   ├── internalGetOrgTier.ts
│   │       │   ├── subscriptionLifecycle.ts
│   │       │   └── webhooks.ts
│   │       ├── certificates/
│   │       │   ├── createCertificate.ts
│   │       │   ├── getCertificate.ts
│   │       │   ├── index.ts
│   │       │   └── restartCertificate.ts
│   │       ├── domain/
│   │       │   ├── checkDomainNamespaceAvailability.ts
│   │       │   ├── index.ts
│   │       │   └── listDomainNamespaces.ts
│   │       ├── external.ts
│   │       ├── generatedLicense/
│   │       │   ├── generateNewEnterpriseLicense.ts
│   │       │   ├── generateNewLicense.ts
│   │       │   ├── index.ts
│   │       │   └── listGeneratedLicenses.ts
│   │       ├── gerbil/
│   │       │   └── createExitNode.ts
│   │       ├── hybrid.ts
│   │       ├── integration.ts
│   │       ├── internal.ts
│   │       ├── license/
│   │       │   ├── activateLicense.ts
│   │       │   ├── deleteLicenseKey.ts
│   │       │   ├── getLicenseStatus.ts
│   │       │   ├── index.ts
│   │       │   ├── listLicenseKeys.ts
│   │       │   └── recheckStatus.ts
│   │       ├── loginPage/
│   │       │   ├── createLoginPage.ts
│   │       │   ├── deleteLoginPage.ts
│   │       │   ├── deleteLoginPageBranding.ts
│   │       │   ├── getLoginPage.ts
│   │       │   ├── getLoginPageBranding.ts
│   │       │   ├── index.ts
│   │       │   ├── loadLoginPage.ts
│   │       │   ├── loadLoginPageBranding.ts
│   │       │   ├── updateLoginPage.ts
│   │       │   └── upsertLoginPageBranding.ts
│   │       ├── misc/
│   │       │   ├── index.ts
│   │       │   └── sendSupportEmail.ts
│   │       ├── org/
│   │       │   ├── index.ts
│   │       │   └── sendUsageNotifications.ts
│   │       ├── orgIdp/
│   │       │   ├── createOrgOidcIdp.ts
│   │       │   ├── deleteOrgIdp.ts
│   │       │   ├── getOrgIdp.ts
│   │       │   ├── index.ts
│   │       │   ├── listOrgIdps.ts
│   │       │   └── updateOrgOidcIdp.ts
│   │       ├── re-key/
│   │       │   ├── index.ts
│   │       │   ├── reGenerateClientSecret.ts
│   │       │   ├── reGenerateExitNodeSecret.ts
│   │       │   └── reGenerateSiteSecret.ts
│   │       ├── remoteExitNode/
│   │       │   ├── createRemoteExitNode.ts
│   │       │   ├── deleteRemoteExitNode.ts
│   │       │   ├── getRemoteExitNode.ts
│   │       │   ├── getRemoteExitNodeToken.ts
│   │       │   ├── handleRemoteExitNodePingMessage.ts
│   │       │   ├── handleRemoteExitNodeRegisterMessage.ts
│   │       │   ├── index.ts
│   │       │   ├── listRemoteExitNodes.ts
│   │       │   ├── pickRemoteExitNodeDefaults.ts
│   │       │   └── quickStartRemoteExitNode.ts
│   │       ├── resource/
│   │       │   ├── getMaintenanceInfo.ts
│   │       │   └── index.ts
│   │       ├── ssh/
│   │       │   ├── index.ts
│   │       │   └── signSshKey.ts
│   │       └── ws/
│   │           ├── index.ts
│   │           ├── messageHandlers.ts
│   │           └── ws.ts
│   ├── routers/
│   │   ├── accessToken/
│   │   │   ├── deleteAccessToken.ts
│   │   │   ├── generateAccessToken.ts
│   │   │   ├── index.ts
│   │   │   └── listAccessTokens.ts
│   │   ├── apiKeys/
│   │   │   ├── createOrgApiKey.ts
│   │   │   ├── createRootApiKey.ts
│   │   │   ├── deleteApiKey.ts
│   │   │   ├── deleteOrgApiKey.ts
│   │   │   ├── getApiKey.ts
│   │   │   ├── index.ts
│   │   │   ├── listApiKeyActions.ts
│   │   │   ├── listOrgApiKeys.ts
│   │   │   ├── listRootApiKeys.ts
│   │   │   ├── setApiKeyActions.ts
│   │   │   └── setApiKeyOrgs.ts
│   │   ├── auditLogs/
│   │   │   ├── exportRequestAuditLog.ts
│   │   │   ├── generateCSV.ts
│   │   │   ├── index.ts
│   │   │   ├── queryRequestAnalytics.ts
│   │   │   ├── queryRequestAuditLog.ts
│   │   │   └── types.ts
│   │   ├── auth/
│   │   │   ├── changePassword.ts
│   │   │   ├── checkResourceSession.ts
│   │   │   ├── deleteMyAccount.ts
│   │   │   ├── disable2fa.ts
│   │   │   ├── index.ts
│   │   │   ├── initialSetupComplete.ts
│   │   │   ├── login.ts
│   │   │   ├── logout.ts
│   │   │   ├── lookupUser.ts
│   │   │   ├── pollDeviceWebAuth.ts
│   │   │   ├── requestEmailVerificationCode.ts
│   │   │   ├── requestPasswordReset.ts
│   │   │   ├── requestTotpSecret.ts
│   │   │   ├── resetPassword.ts
│   │   │   ├── securityKey.ts
│   │   │   ├── setServerAdmin.ts
│   │   │   ├── signup.ts
│   │   │   ├── startDeviceWebAuth.ts
│   │   │   ├── types.ts
│   │   │   ├── validateSetupToken.ts
│   │   │   ├── verifyDeviceWebAuth.ts
│   │   │   ├── verifyEmail.ts
│   │   │   └── verifyTotp.ts
│   │   ├── badger/
│   │   │   ├── exchangeSession.ts
│   │   │   ├── index.ts
│   │   │   ├── logRequestAudit.ts
│   │   │   ├── verifySession.test.ts
│   │   │   └── verifySession.ts
│   │   ├── billing/
│   │   │   ├── types.ts
│   │   │   └── webhooks.ts
│   │   ├── blueprints/
│   │   │   ├── applyJSONBlueprint.ts
│   │   │   ├── applyYAMLBlueprint.ts
│   │   │   ├── getBlueprint.ts
│   │   │   ├── index.ts
│   │   │   ├── listBlueprints.ts
│   │   │   └── types.ts
│   │   ├── certificates/
│   │   │   ├── createCertificate.ts
│   │   │   └── types.ts
│   │   ├── client/
│   │   │   ├── archiveClient.ts
│   │   │   ├── blockClient.ts
│   │   │   ├── createClient.ts
│   │   │   ├── createUserClient.ts
│   │   │   ├── deleteClient.ts
│   │   │   ├── getClient.ts
│   │   │   ├── index.ts
│   │   │   ├── listClients.ts
│   │   │   ├── listUserDevices.ts
│   │   │   ├── pickClientDefaults.ts
│   │   │   ├── targets.ts
│   │   │   ├── terminate.ts
│   │   │   ├── unarchiveClient.ts
│   │   │   ├── unblockClient.ts
│   │   │   └── updateClient.ts
│   │   ├── domain/
│   │   │   ├── createOrgDomain.ts
│   │   │   ├── deleteOrgDomain.ts
│   │   │   ├── getDNSRecords.ts
│   │   │   ├── getDomain.ts
│   │   │   ├── index.ts
│   │   │   ├── listDomains.ts
│   │   │   ├── restartOrgDomain.ts
│   │   │   ├── types.ts
│   │   │   └── updateDomain.ts
│   │   ├── external.ts
│   │   ├── generatedLicense/
│   │   │   └── types.ts
│   │   ├── gerbil/
│   │   │   ├── createExitNode.ts
│   │   │   ├── getAllRelays.ts
│   │   │   ├── getConfig.ts
│   │   │   ├── getResolvedHostname.ts
│   │   │   ├── index.ts
│   │   │   ├── peers.ts
│   │   │   ├── receiveBandwidth.ts
│   │   │   └── updateHolePunch.ts
│   │   ├── hybrid.ts
│   │   ├── idp/
│   │   │   ├── createIdpOrgPolicy.ts
│   │   │   ├── createOidcIdp.ts
│   │   │   ├── deleteIdp.ts
│   │   │   ├── deleteIdpOrgPolicy.ts
│   │   │   ├── generateOidcUrl.ts
│   │   │   ├── getIdp.ts
│   │   │   ├── index.ts
│   │   │   ├── listIdpOrgPolicies.ts
│   │   │   ├── listIdps.ts
│   │   │   ├── updateIdpOrgPolicy.ts
│   │   │   ├── updateOidcIdp.ts
│   │   │   └── validateOidcCallback.ts
│   │   ├── integration.ts
│   │   ├── internal.ts
│   │   ├── license/
│   │   │   └── types.ts
│   │   ├── loginPage/
│   │   │   └── types.ts
│   │   ├── newt/
│   │   │   ├── buildConfiguration.ts
│   │   │   ├── createNewt.ts
│   │   │   ├── dockerSocket.ts
│   │   │   ├── getNewtToken.ts
│   │   │   ├── handleApplyBlueprintMessage.ts
│   │   │   ├── handleGetConfigMessage.ts
│   │   │   ├── handleNewtDisconnectingMessage.ts
│   │   │   ├── handleNewtPingMessage.ts
│   │   │   ├── handleNewtPingRequestMessage.ts
│   │   │   ├── handleNewtRegisterMessage.ts
│   │   │   ├── handleReceiveBandwidthMessage.ts
│   │   │   ├── handleSocketMessages.ts
│   │   │   ├── index.ts
│   │   │   ├── peers.ts
│   │   │   ├── sync.ts
│   │   │   └── targets.ts
│   │   ├── olm/
│   │   │   ├── archiveUserOlm.ts
│   │   │   ├── buildConfiguration.ts
│   │   │   ├── createOlm.ts
│   │   │   ├── createUserOlm.ts
│   │   │   ├── deleteUserOlm.ts
│   │   │   ├── error.ts
│   │   │   ├── fingerprintingUtils.ts
│   │   │   ├── getOlmToken.ts
│   │   │   ├── getUserOlm.ts
│   │   │   ├── handleOlmDisconnectingMessage.ts
│   │   │   ├── handleOlmPingMessage.ts
│   │   │   ├── handleOlmRegisterMessage.ts
│   │   │   ├── handleOlmRelayMessage.ts
│   │   │   ├── handleOlmServerInitAddPeerHandshake.ts
│   │   │   ├── handleOlmServerPeerAddMessage.ts
│   │   │   ├── handleOlmUnRelayMessage.ts
│   │   │   ├── index.ts
│   │   │   ├── listUserOlms.ts
│   │   │   ├── peers.ts
│   │   │   ├── recoverOlmWithFingerprint.ts
│   │   │   ├── sync.ts
│   │   │   └── unarchiveUserOlm.ts
│   │   ├── org/
│   │   │   ├── checkId.ts
│   │   │   ├── checkOrgUserAccess.ts
│   │   │   ├── createOrg.ts
│   │   │   ├── deleteOrg.ts
│   │   │   ├── getOrg.ts
│   │   │   ├── getOrgOverview.ts
│   │   │   ├── index.ts
│   │   │   ├── listOrgs.ts
│   │   │   ├── listUserOrgs.ts
│   │   │   ├── pickOrgDefaults.ts
│   │   │   ├── resetOrgBandwidth.ts
│   │   │   └── updateOrg.ts
│   │   ├── orgIdp/
│   │   │   └── types.ts
│   │   ├── remoteExitNode/
│   │   │   └── types.ts
│   │   ├── resource/
│   │   │   ├── addEmailToResourceWhitelist.ts
│   │   │   ├── addRoleToResource.ts
│   │   │   ├── addUserToResource.ts
│   │   │   ├── authWithAccessToken.ts
│   │   │   ├── authWithPassword.ts
│   │   │   ├── authWithPincode.ts
│   │   │   ├── authWithWhitelist.ts
│   │   │   ├── createResource.ts
│   │   │   ├── createResourceRule.ts
│   │   │   ├── deleteResource.ts
│   │   │   ├── deleteResourceRule.ts
│   │   │   ├── getExchangeToken.ts
│   │   │   ├── getResource.ts
│   │   │   ├── getResourceAuthInfo.ts
│   │   │   ├── getResourceWhitelist.ts
│   │   │   ├── getUserResources.ts
│   │   │   ├── index.ts
│   │   │   ├── listAllResourceNames.ts
│   │   │   ├── listResourceRoles.ts
│   │   │   ├── listResourceRules.ts
│   │   │   ├── listResourceUsers.ts
│   │   │   ├── listResources.ts
│   │   │   ├── removeEmailFromResourceWhitelist.ts
│   │   │   ├── removeRoleFromResource.ts
│   │   │   ├── removeUserFromResource.ts
│   │   │   ├── setResourceHeaderAuth.ts
│   │   │   ├── setResourcePassword.ts
│   │   │   ├── setResourcePincode.ts
│   │   │   ├── setResourceRoles.ts
│   │   │   ├── setResourceUsers.ts
│   │   │   ├── setResourceWhitelist.ts
│   │   │   ├── types.ts
│   │   │   ├── updateResource.ts
│   │   │   └── updateResourceRule.ts
│   │   ├── role/
│   │   │   ├── addRoleAction.ts
│   │   │   ├── addRoleSite.ts
│   │   │   ├── createRole.ts
│   │   │   ├── deleteRole.ts
│   │   │   ├── getRole.ts
│   │   │   ├── index.ts
│   │   │   ├── listRoleActions.ts
│   │   │   ├── listRoleResources.ts
│   │   │   ├── listRoleSites.ts
│   │   │   ├── listRoles.ts
│   │   │   ├── removeRoleAction.ts
│   │   │   ├── removeRoleResource.ts
│   │   │   ├── removeRoleSite.ts
│   │   │   └── updateRole.ts
│   │   ├── serverInfo/
│   │   │   ├── getServerInfo.ts
│   │   │   └── index.ts
│   │   ├── site/
│   │   │   ├── createSite.ts
│   │   │   ├── deleteSite.ts
│   │   │   ├── getSite.ts
│   │   │   ├── index.ts
│   │   │   ├── listSiteRoles.ts
│   │   │   ├── listSites.ts
│   │   │   ├── pickSiteDefaults.ts
│   │   │   ├── socketIntegration.ts
│   │   │   └── updateSite.ts
│   │   ├── siteResource/
│   │   │   ├── addClientToSiteResource.ts
│   │   │   ├── addRoleToSiteResource.ts
│   │   │   ├── addUserToSiteResource.ts
│   │   │   ├── batchAddClientToSiteResources.ts
│   │   │   ├── createSiteResource.ts
│   │   │   ├── deleteSiteResource.ts
│   │   │   ├── getSiteResource.ts
│   │   │   ├── index.ts
│   │   │   ├── listAllSiteResourcesByOrg.ts
│   │   │   ├── listSiteResourceClients.ts
│   │   │   ├── listSiteResourceRoles.ts
│   │   │   ├── listSiteResourceUsers.ts
│   │   │   ├── listSiteResources.ts
│   │   │   ├── removeClientFromSiteResource.ts
│   │   │   ├── removeRoleFromSiteResource.ts
│   │   │   ├── removeUserFromSiteResource.ts
│   │   │   ├── setSiteResourceClients.ts
│   │   │   ├── setSiteResourceRoles.ts
│   │   │   ├── setSiteResourceUsers.ts
│   │   │   └── updateSiteResource.ts
│   │   ├── supporterKey/
│   │   │   ├── hideSupporterKey.ts
│   │   │   ├── index.ts
│   │   │   ├── isSupporterKeyVisible.ts
│   │   │   └── validateSupporterKey.ts
│   │   ├── target/
│   │   │   ├── createTarget.ts
│   │   │   ├── deleteTarget.ts
│   │   │   ├── getTarget.ts
│   │   │   ├── handleHealthcheckStatusMessage.ts
│   │   │   ├── helpers.ts
│   │   │   ├── index.ts
│   │   │   ├── listTargets.ts
│   │   │   └── updateTarget.ts
│   │   ├── traefik/
│   │   │   ├── configSchema.ts
│   │   │   ├── index.ts
│   │   │   └── traefikConfigProvider.ts
│   │   ├── user/
│   │   │   ├── acceptInvite.ts
│   │   │   ├── addUserAction.ts
│   │   │   ├── addUserRole.ts
│   │   │   ├── addUserSite.ts
│   │   │   ├── adminGeneratePasswordResetCode.ts
│   │   │   ├── adminGetUser.ts
│   │   │   ├── adminListUsers.ts
│   │   │   ├── adminRemoveUser.ts
│   │   │   ├── adminUpdateUser2FA.ts
│   │   │   ├── createOrgUser.ts
│   │   │   ├── getOrgUser.ts
│   │   │   ├── getOrgUserByUsername.ts
│   │   │   ├── getUser.ts
│   │   │   ├── index.ts
│   │   │   ├── inviteUser.ts
│   │   │   ├── listInvitations.ts
│   │   │   ├── listUsers.ts
│   │   │   ├── myDevice.ts
│   │   │   ├── removeInvitation.ts
│   │   │   ├── removeUserAction.ts
│   │   │   ├── removeUserOrg.ts
│   │   │   ├── removeUserResource.ts
│   │   │   ├── removeUserSite.ts
│   │   │   └── updateOrgUser.ts
│   │   └── ws/
│   │       ├── checkRoundTripMessage.ts
│   │       ├── handleRoundTripMessage.ts
│   │       ├── index.ts
│   │       ├── messageHandlers.ts
│   │       ├── types.ts
│   │       └── ws.ts
│   ├── setup/
│   │   ├── .gitignore
│   │   ├── clearStaleData.ts
│   │   ├── copyInConfig.ts
│   │   ├── ensureActions.ts
│   │   ├── ensureSetupToken.ts
│   │   ├── index.ts
│   │   ├── migrationsPg.ts
│   │   ├── migrationsSqlite.ts
│   │   ├── scriptsPg/
│   │   │   ├── 1.10.0.ts
│   │   │   ├── 1.10.2.ts
│   │   │   ├── 1.11.0.ts
│   │   │   ├── 1.11.1.ts
│   │   │   ├── 1.12.0.ts
│   │   │   ├── 1.13.0.ts
│   │   │   ├── 1.14.0.ts
│   │   │   ├── 1.15.0.ts
│   │   │   ├── 1.15.3.ts
│   │   │   ├── 1.15.4.ts
│   │   │   ├── 1.16.0.ts
│   │   │   ├── 1.6.0.ts
│   │   │   ├── 1.7.0.ts
│   │   │   ├── 1.8.0.ts
│   │   │   └── 1.9.0.ts
│   │   └── scriptsSqlite/
│   │       ├── 1.0.0-beta1.ts
│   │       ├── 1.0.0-beta10.ts
│   │       ├── 1.0.0-beta12.ts
│   │       ├── 1.0.0-beta13.ts
│   │       ├── 1.0.0-beta15.ts
│   │       ├── 1.0.0-beta2.ts
│   │       ├── 1.0.0-beta3.ts
│   │       ├── 1.0.0-beta5.ts
│   │       ├── 1.0.0-beta6.ts
│   │       ├── 1.0.0-beta9.ts
│   │       ├── 1.0.0.ts
│   │       ├── 1.1.0.ts
│   │       ├── 1.10.0.ts
│   │       ├── 1.10.1.ts
│   │       ├── 1.10.2.ts
│   │       ├── 1.11.0.ts
│   │       ├── 1.11.1.ts
│   │       ├── 1.12.0.ts
│   │       ├── 1.13.0.ts
│   │       ├── 1.14.0.ts
│   │       ├── 1.15.0.ts
│   │       ├── 1.15.3.ts
│   │       ├── 1.15.4.ts
│   │       ├── 1.16.0.ts
│   │       ├── 1.2.0.ts
│   │       ├── 1.3.0.ts
│   │       ├── 1.5.0.ts
│   │       ├── 1.6.0.ts
│   │       ├── 1.7.0.ts
│   │       ├── 1.8.0.ts
│   │       └── 1.9.0.ts
│   └── types/
│       ├── ArrayElement.ts
│       ├── Auth.ts
│       ├── ErrorResponse.ts
│       ├── HttpCode.ts
│       ├── MessageResponse.ts
│       ├── Pagination.ts
│       ├── Response.ts
│       ├── Tiers.ts
│       └── UserTypes.ts
├── src/
│   ├── actions/
│   │   └── server.ts
│   ├── app/
│   │   ├── [orgId]/
│   │   │   ├── layout.tsx
│   │   │   ├── page.tsx
│   │   │   └── settings/
│   │   │       ├── (private)/
│   │   │       │   ├── access/
│   │   │       │   │   └── approvals/
│   │   │       │   │       └── page.tsx
│   │   │       │   ├── billing/
│   │   │       │   │   ├── layout.tsx
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── idp/
│   │   │       │   │   ├── [idpId]/
│   │   │       │   │   │   ├── general/
│   │   │       │   │   │   │   └── page.tsx
│   │   │       │   │   │   ├── layout.tsx
│   │   │       │   │   │   └── page.tsx
│   │   │       │   │   ├── create/
│   │   │       │   │   │   └── page.tsx
│   │   │       │   │   ├── layout.tsx
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── license/
│   │   │       │   │   ├── layout.tsx
│   │   │       │   │   └── page.tsx
│   │   │       │   └── remote-exit-nodes/
│   │   │       │       ├── [remoteExitNodeId]/
│   │   │       │       │   ├── credentials/
│   │   │       │       │   │   └── page.tsx
│   │   │       │       │   ├── layout.tsx
│   │   │       │       │   └── page.tsx
│   │   │       │       ├── create/
│   │   │       │       │   └── page.tsx
│   │   │       │       └── page.tsx
│   │   │       ├── access/
│   │   │       │   ├── invitations/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── layout.tsx
│   │   │       │   ├── page.tsx
│   │   │       │   ├── roles/
│   │   │       │   │   └── page.tsx
│   │   │       │   └── users/
│   │   │       │       ├── [userId]/
│   │   │       │       │   ├── access-controls/
│   │   │       │       │   │   └── page.tsx
│   │   │       │       │   ├── layout.tsx
│   │   │       │       │   └── page.tsx
│   │   │       │       ├── create/
│   │   │       │       │   └── page.tsx
│   │   │       │       └── page.tsx
│   │   │       ├── api-keys/
│   │   │       │   ├── [apiKeyId]/
│   │   │       │   │   ├── layout.tsx
│   │   │       │   │   ├── page.tsx
│   │   │       │   │   └── permissions/
│   │   │       │   │       └── page.tsx
│   │   │       │   ├── create/
│   │   │       │   │   └── page.tsx
│   │   │       │   └── page.tsx
│   │   │       ├── blueprints/
│   │   │       │   ├── [blueprintId]/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── create/
│   │   │       │   │   └── page.tsx
│   │   │       │   └── page.tsx
│   │   │       ├── clients/
│   │   │       │   ├── layout.tsx
│   │   │       │   ├── machine/
│   │   │       │   │   ├── [niceId]/
│   │   │       │   │   │   ├── credentials/
│   │   │       │   │   │   │   └── page.tsx
│   │   │       │   │   │   ├── general/
│   │   │       │   │   │   │   └── page.tsx
│   │   │       │   │   │   ├── layout.tsx
│   │   │       │   │   │   └── page.tsx
│   │   │       │   │   ├── create/
│   │   │       │   │   │   └── page.tsx
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── page.tsx
│   │   │       │   └── user/
│   │   │       │       ├── [niceId]/
│   │   │       │       │   ├── general/
│   │   │       │       │   │   └── page.tsx
│   │   │       │       │   ├── layout.tsx
│   │   │       │       │   └── page.tsx
│   │   │       │       └── page.tsx
│   │   │       ├── domains/
│   │   │       │   ├── [domainId]/
│   │   │       │   │   └── page.tsx
│   │   │       │   └── page.tsx
│   │   │       ├── general/
│   │   │       │   ├── auth-page/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── layout.tsx
│   │   │       │   ├── page.tsx
│   │   │       │   └── security/
│   │   │       │       └── page.tsx
│   │   │       ├── layout.tsx
│   │   │       ├── logs/
│   │   │       │   ├── access/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── action/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── analytics/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── layout.tsx
│   │   │       │   ├── page.tsx
│   │   │       │   └── request/
│   │   │       │       └── page.tsx
│   │   │       ├── not-found.tsx
│   │   │       ├── page.tsx
│   │   │       ├── resources/
│   │   │       │   ├── client/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── page.tsx
│   │   │       │   └── proxy/
│   │   │       │       ├── [niceId]/
│   │   │       │       │   ├── authentication/
│   │   │       │       │   │   └── page.tsx
│   │   │       │       │   ├── general/
│   │   │       │       │   │   └── page.tsx
│   │   │       │       │   ├── layout.tsx
│   │   │       │       │   ├── page.tsx
│   │   │       │       │   ├── proxy/
│   │   │       │       │   │   └── page.tsx
│   │   │       │       │   └── rules/
│   │   │       │       │       └── page.tsx
│   │   │       │       ├── create/
│   │   │       │       │   └── page.tsx
│   │   │       │       └── page.tsx
│   │   │       ├── share-links/
│   │   │       │   └── page.tsx
│   │   │       └── sites/
│   │   │           ├── [niceId]/
│   │   │           │   ├── credentials/
│   │   │           │   │   └── page.tsx
│   │   │           │   ├── general/
│   │   │           │   │   └── page.tsx
│   │   │           │   ├── layout.tsx
│   │   │           │   ├── page.tsx
│   │   │           │   └── wireguardConfig.ts
│   │   │           ├── create/
│   │   │           │   └── page.tsx
│   │   │           └── page.tsx
│   │   ├── admin/
│   │   │   ├── api-keys/
│   │   │   │   ├── [apiKeyId]/
│   │   │   │   │   ├── layout.tsx
│   │   │   │   │   ├── page.tsx
│   │   │   │   │   └── permissions/
│   │   │   │   │       └── page.tsx
│   │   │   │   ├── create/
│   │   │   │   │   └── page.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── idp/
│   │   │   │   ├── [idpId]/
│   │   │   │   │   ├── general/
│   │   │   │   │   │   └── page.tsx
│   │   │   │   │   ├── layout.tsx
│   │   │   │   │   ├── page.tsx
│   │   │   │   │   └── policies/
│   │   │   │   │       └── page.tsx
│   │   │   │   ├── create/
│   │   │   │   │   └── page.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── layout.tsx
│   │   │   ├── license/
│   │   │   │   ├── layout.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── page.tsx
│   │   │   └── users/
│   │   │       ├── AdminUsersTable.tsx
│   │   │       ├── [userId]/
│   │   │       │   ├── general/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── layout.tsx
│   │   │       │   └── page.tsx
│   │   │       └── page.tsx
│   │   ├── auth/
│   │   │   ├── 2fa/
│   │   │   │   └── setup/
│   │   │   │       └── page.tsx
│   │   │   ├── delete-account/
│   │   │   │   ├── DeleteAccountClient.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── idp/
│   │   │   │   └── [idpId]/
│   │   │   │       └── oidc/
│   │   │   │           └── callback/
│   │   │   │               └── page.tsx
│   │   │   ├── initial-setup/
│   │   │   │   ├── layout.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── layout.tsx
│   │   │   ├── login/
│   │   │   │   ├── device/
│   │   │   │   │   ├── page.tsx
│   │   │   │   │   └── success/
│   │   │   │   │       └── page.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── org/
│   │   │   │   ├── [orgId]/
│   │   │   │   │   └── page.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── reset-password/
│   │   │   │   ├── ResetPasswordForm.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── resource/
│   │   │   │   └── [resourceGuid]/
│   │   │   │       └── page.tsx
│   │   │   ├── signup/
│   │   │   │   └── page.tsx
│   │   │   └── verify-email/
│   │   │       └── page.tsx
│   │   ├── globals.css
│   │   ├── invite/
│   │   │   └── page.tsx
│   │   ├── layout.tsx
│   │   ├── maintenance-screen/
│   │   │   └── page.tsx
│   │   ├── navigation.tsx
│   │   ├── not-found.tsx
│   │   ├── page.tsx
│   │   ├── robots.ts
│   │   ├── s/
│   │   │   └── [accessToken]/
│   │   │       └── page.tsx
│   │   └── setup/
│   │       ├── layout.tsx
│   │       └── page.tsx
│   ├── components/
│   │   ├── AccessPageHeaderAndNav.tsx
│   │   ├── AccessToken.tsx
│   │   ├── AccessTokenUsage.tsx
│   │   ├── ActionBanner.tsx
│   │   ├── AdminIdpDataTable.tsx
│   │   ├── AdminIdpTable.tsx
│   │   ├── AdminUsersDataTable.tsx
│   │   ├── AdminUsersTable.tsx
│   │   ├── ApiKeysDataTable.tsx
│   │   ├── ApiKeysTable.tsx
│   │   ├── ApplyInternalRedirect.tsx
│   │   ├── ApprovalFeed.tsx
│   │   ├── ApprovalsBanner.tsx
│   │   ├── ApprovalsEmptyState.tsx
│   │   ├── AuthPageBrandingForm.tsx
│   │   ├── AuthPageSettings.tsx
│   │   ├── AutoLoginHandler.tsx
│   │   ├── AutoProvisionConfigWidget.tsx
│   │   ├── BlueprintDetailsForm.tsx
│   │   ├── BlueprintsTable.tsx
│   │   ├── BrandingLogo.tsx
│   │   ├── CertificateStatus.tsx
│   │   ├── ChangePasswordDialog.tsx
│   │   ├── ChangePasswordForm.tsx
│   │   ├── ClientDownloadBanner.tsx
│   │   ├── ClientInfoCard.tsx
│   │   ├── ClientResourcesTable.tsx
│   │   ├── ClientsDataTable.tsx
│   │   ├── ColumnFilter.tsx
│   │   ├── ColumnFilterButton.tsx
│   │   ├── ConfirmDeleteDialog.tsx
│   │   ├── ContainersSelector.tsx
│   │   ├── CopyTextBox.tsx
│   │   ├── CopyToClipboard.tsx
│   │   ├── CreateBlueprintForm.tsx
│   │   ├── CreateDomainForm.tsx
│   │   ├── CreateInternalResourceDialog.tsx
│   │   ├── CreateRoleForm.tsx
│   │   ├── CreateShareLinkForm.tsx
│   │   ├── Credenza.tsx
│   │   ├── CustomDomainInput.tsx
│   │   ├── DNSRecordTable.tsx
│   │   ├── DNSRecordsDataTable.tsx
│   │   ├── DashboardLoginForm.tsx
│   │   ├── DataTablePagination.tsx
│   │   ├── DateTimePicker.tsx
│   │   ├── DeleteAccountConfirmDialog.tsx
│   │   ├── DeleteRoleForm.tsx
│   │   ├── DeviceAuthConfirmation.tsx
│   │   ├── DeviceLoginForm.tsx
│   │   ├── Disable2FaForm.tsx
│   │   ├── DismissableBanner.tsx
│   │   ├── DomainCertForm.tsx
│   │   ├── DomainInfoCard.tsx
│   │   ├── DomainPicker.tsx
│   │   ├── DomainsDataTable.tsx
│   │   ├── DomainsTable.tsx
│   │   ├── EditInternalResourceDialog.tsx
│   │   ├── EditRoleForm.tsx
│   │   ├── Enable2FaDialog.tsx
│   │   ├── Enable2FaForm.tsx
│   │   ├── ExitNodeInfoCard.tsx
│   │   ├── ExitNodesDataTable.tsx
│   │   ├── ExitNodesTable.tsx
│   │   ├── GenerateLicenseKeyForm.tsx
│   │   ├── GenerateLicenseKeysTable.tsx
│   │   ├── HeadersInput.tsx
│   │   ├── HealthCheckDialog.tsx
│   │   ├── HorizontalTabs.tsx
│   │   ├── IdpCreateWizard.tsx
│   │   ├── IdpGlobalModeBanner.tsx
│   │   ├── IdpLoginButtons.tsx
│   │   ├── IdpTypeBadge.tsx
│   │   ├── InfoSection.tsx
│   │   ├── InternalResourceForm.tsx
│   │   ├── InvitationsDataTable.tsx
│   │   ├── InvitationsTable.tsx
│   │   ├── InviteStatusCard.tsx
│   │   ├── Layout.tsx
│   │   ├── LayoutHeader.tsx
│   │   ├── LayoutMobileMenu.tsx
│   │   ├── LayoutSidebar.tsx
│   │   ├── LicenseKeysDataTable.tsx
│   │   ├── LicenseViolation.tsx
│   │   ├── LocaleSwitcher.tsx
│   │   ├── LocaleSwitcherSelect.tsx
│   │   ├── LogAnalyticsData.tsx
│   │   ├── LogDataTable.tsx
│   │   ├── LoginCardHeader.tsx
│   │   ├── LoginForm.tsx
│   │   ├── LoginOrgSelector.tsx
│   │   ├── LoginPasswordForm.tsx
│   │   ├── MachineClientsBanner.tsx
│   │   ├── MachineClientsTable.tsx
│   │   ├── MemberResourcesPortal.tsx
│   │   ├── MfaInputForm.tsx
│   │   ├── NewPricingLicenseForm.tsx
│   │   ├── OptionSelect.tsx
│   │   ├── OrgApiKeysDataTable.tsx
│   │   ├── OrgApiKeysTable.tsx
│   │   ├── OrgIdpDataTable.tsx
│   │   ├── OrgIdpTable.tsx
│   │   ├── OrgInfoCard.tsx
│   │   ├── OrgLoginPage.tsx
│   │   ├── OrgPolicyRequired.tsx
│   │   ├── OrgPolicyResult.tsx
│   │   ├── OrgSelectionForm.tsx
│   │   ├── OrgSelector.tsx
│   │   ├── OrgSignInLink.tsx
│   │   ├── OrganizationLanding.tsx
│   │   ├── OrganizationLandingCard.tsx
│   │   ├── PaidFeaturesAlert.tsx
│   │   ├── PathMatchRenameModal.tsx
│   │   ├── PermissionsSelectBox.tsx
│   │   ├── PlaceHolderLoader.tsx
│   │   ├── PolicyDataTable.tsx
│   │   ├── PolicyTable.tsx
│   │   ├── PrivateResourcesBanner.tsx
│   │   ├── ProductUpdates.tsx
│   │   ├── ProfessionalContentOverlay.tsx
│   │   ├── ProfileIcon.tsx
│   │   ├── ProxyResourcesBanner.tsx
│   │   ├── ProxyResourcesTable.tsx
│   │   ├── QRContainer.tsx
│   │   ├── RedirectToOrg.tsx
│   │   ├── RefreshButton.tsx
│   │   ├── RegenerateInvitationForm.tsx
│   │   ├── RegionSelector.tsx
│   │   ├── ResetPasswordForm.tsx
│   │   ├── ResourceAccessDenied.tsx
│   │   ├── ResourceAuthPortal.tsx
│   │   ├── ResourceInfoBox.tsx
│   │   ├── ResourceNotFound.tsx
│   │   ├── RestartDomainButton.tsx
│   │   ├── RoleForm.tsx
│   │   ├── RolesDataTable.tsx
│   │   ├── RolesTable.tsx
│   │   ├── SecurityKeyAuthButton.tsx
│   │   ├── SecurityKeyForm.tsx
│   │   ├── SetLastOrgCookie.tsx
│   │   ├── SetResourceHeaderAuthForm.tsx
│   │   ├── SetResourcePasswordForm.tsx
│   │   ├── SetResourcePincodeForm.tsx
│   │   ├── Settings.tsx
│   │   ├── SettingsSectionTitle.tsx
│   │   ├── ShareLinksDataTable.tsx
│   │   ├── ShareLinksSplash.tsx
│   │   ├── ShareLinksTable.tsx
│   │   ├── SidebarLicenseButton.tsx
│   │   ├── SidebarNav.tsx
│   │   ├── SidebarSupportButton.tsx
│   │   ├── SignupForm.tsx
│   │   ├── SiteInfoCard.tsx
│   │   ├── SitePriceCalculator.tsx
│   │   ├── SitesBanner.tsx
│   │   ├── SitesSplashCard.tsx
│   │   ├── SitesTable.tsx
│   │   ├── SmartLoginForm.tsx
│   │   ├── SplashImage.tsx
│   │   ├── StoreInternalRedirect.tsx
│   │   ├── StrategySelect.tsx
│   │   ├── SubscriptionViolation.tsx
│   │   ├── SupporterMessage.tsx
│   │   ├── SupporterStatus.tsx
│   │   ├── SwitchInput.tsx
│   │   ├── TailwindIndicator.tsx
│   │   ├── TanstackQueryProvider.tsx
│   │   ├── ThemeSwitcher.tsx
│   │   ├── TopbarNav.tsx
│   │   ├── Toploader.tsx
│   │   ├── TwoFactorSetupForm.tsx
│   │   ├── UserDevicesTable.tsx
│   │   ├── UserProfileCard.tsx
│   │   ├── UsersDataTable.tsx
│   │   ├── UsersTable.tsx
│   │   ├── ValidateOidcToken.tsx
│   │   ├── ValidateSessionTransferToken.tsx
│   │   ├── VerifyEmailForm.tsx
│   │   ├── ViewDevicesDialog.tsx
│   │   ├── ViewportHeightFix.tsx
│   │   ├── WorldMap.tsx
│   │   ├── newt-install-commands.tsx
│   │   ├── olm-install-commands.tsx
│   │   ├── resource-target-address-item.tsx
│   │   ├── tags/
│   │   │   ├── autocomplete.tsx
│   │   │   ├── tag-input.tsx
│   │   │   ├── tag-list.tsx
│   │   │   ├── tag-popover.tsx
│   │   │   └── tag.tsx
│   │   └── ui/
│   │       ├── alert.tsx
│   │       ├── avatar.tsx
│   │       ├── badge.tsx
│   │       ├── breadcrumb.tsx
│   │       ├── button.tsx
│   │       ├── calendar.tsx
│   │       ├── card.tsx
│   │       ├── chart.tsx
│   │       ├── checkbox.tsx
│   │       ├── collapsible.tsx
│   │       ├── command.tsx
│   │       ├── controlled-data-table.tsx
│   │       ├── data-table.tsx
│   │       ├── dialog.tsx
│   │       ├── drawer.tsx
│   │       ├── dropdown-menu.tsx
│   │       ├── form.tsx
│   │       ├── info-popup.tsx
│   │       ├── input-otp.tsx
│   │       ├── input.tsx
│   │       ├── label.tsx
│   │       ├── popover.tsx
│   │       ├── progress.tsx
│   │       ├── radio-group.tsx
│   │       ├── scroll-area.tsx
│   │       ├── select.tsx
│   │       ├── separator.tsx
│   │       ├── sheet.tsx
│   │       ├── switch.tsx
│   │       ├── table.tsx
│   │       ├── tabs.tsx
│   │       ├── textarea.tsx
│   │       ├── toast.tsx
│   │       ├── toaster.tsx
│   │       └── tooltip.tsx
│   ├── contexts/
│   │   ├── apiKeyContext.ts
│   │   ├── clientContext.ts
│   │   ├── domainContext.ts
│   │   ├── envContext.ts
│   │   ├── licenseStatusContext.ts
│   │   ├── orgContext.ts
│   │   ├── orgUserContext.ts
│   │   ├── remoteExitNodeContext.ts
│   │   ├── resourceContext.ts
│   │   ├── siteContext.ts
│   │   ├── subscriptionStatusContext.ts
│   │   ├── supporterStatusContext.ts
│   │   └── userContext.ts
│   ├── hooks/
│   │   ├── useApikeyContext.ts
│   │   ├── useCertificate.ts
│   │   ├── useClientContext.ts
│   │   ├── useDomainContext.ts
│   │   ├── useEnvContext.ts
│   │   ├── useLicenseStatusContext.ts
│   │   ├── useLocalStorage.ts
│   │   ├── useMediaQuery.ts
│   │   ├── useNavigationContext.ts
│   │   ├── useOrgContext.ts
│   │   ├── useOrgUserContext.ts
│   │   ├── usePaidStatus.ts
│   │   ├── useRemoteExitNodeContext.ts
│   │   ├── useResourceContext.ts
│   │   ├── useSiteContext.ts
│   │   ├── useStoredColumnVisibility.ts
│   │   ├── useStoredPageSize.ts
│   │   ├── useSubscriptionStatusContext.ts
│   │   ├── useSupporterStatusContext.ts
│   │   ├── useToast.ts
│   │   ├── useUserContext.ts
│   │   └── useUserLookup.ts
│   ├── i18n/
│   │   ├── config.ts
│   │   └── request.ts
│   ├── lib/
│   │   ├── api/
│   │   │   ├── cookies.ts
│   │   │   ├── formatAxiosError.ts
│   │   │   ├── getCachedOrg.ts
│   │   │   ├── getCachedOrgUser.ts
│   │   │   ├── getCachedSubscription.ts
│   │   │   ├── index.ts
│   │   │   └── isOrgSubscribed.ts
│   │   ├── auth/
│   │   │   └── verifySession.ts
│   │   ├── cleanRedirect.ts
│   │   ├── cn.ts
│   │   ├── countryCodeList.ts
│   │   ├── countryCodeToFlagEmoji.ts
│   │   ├── dataSize.ts
│   │   ├── docker.ts
│   │   ├── durationToMs.ts
│   │   ├── formatDeviceFingerprint.ts
│   │   ├── getSevenDaysAgo.ts
│   │   ├── getUserDisplayName.ts
│   │   ├── internalRedirect.ts
│   │   ├── parseHostTarget.ts
│   │   ├── pullEnv.ts
│   │   ├── queries.ts
│   │   ├── replacePlaceholder.ts
│   │   ├── shareLinks.ts
│   │   ├── sortColumn.ts
│   │   ├── subdomain-utils.ts
│   │   ├── themeColors.ts
│   │   ├── timeAgoFormatter.ts
│   │   ├── types/
│   │   │   ├── env.ts
│   │   │   └── sort.ts
│   │   ├── validateLocalPath.ts
│   │   ├── wait.ts
│   │   └── wireguard.ts
│   ├── middleware.ts
│   ├── providers/
│   │   ├── ApiKeyProvider.tsx
│   │   ├── ClientProvider.tsx
│   │   ├── DomainProvider.tsx
│   │   ├── EnvProvider.tsx
│   │   ├── LicenseStatusProvider.tsx
│   │   ├── OrgProvider.tsx
│   │   ├── OrgUserProvider.tsx
│   │   ├── RemoteExitNodeProvider.tsx
│   │   ├── ResourceProvider.tsx
│   │   ├── SiteProvider.tsx
│   │   ├── SubscriptionStatusProvider.tsx
│   │   ├── SupporterStatusProvider.tsx
│   │   ├── ThemeDataProvider.tsx
│   │   ├── ThemeProvider.tsx
│   │   └── UserProvider.tsx
│   ├── services/
│   │   └── locale.ts
│   └── types/
│       ├── canvas-confetti.d.ts
│       └── tanstack-query.d.ts
├── test/
│   └── assert.ts
├── tsconfig.enterprise.json
├── tsconfig.oss.json
└── tsconfig.saas.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .dockerignore
================================================
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
/coverage
/.next/
/out/
/build
.DS_Store
*.pem
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.env*.local
.env
.vercel
*.tsbuildinfo
next-env.d.ts
*.db
*.sqlite
*.sqlite3
*.log
.machinelogs*.json
*-audit.json
install/
bruno/
LICENSE
CONTRIBUTING.md
dist
.git
server/migrations/
config/
build.ts
tsconfig.json
Dockerfile*
drizzle.config.ts


================================================
FILE: .editorconfig
================================================
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true

[*.ts]
quote_type = double

[*.md]
max_line_length = off
trim_trailing_whitespace = false


================================================
FILE: .eslintrc.json
================================================
{
    "extends": ["next/core-web-vitals", "next/typescript"]
}


================================================
FILE: .github/DISCUSSION_TEMPLATE/feature-requests.yml
================================================
body:
    - type: textarea
      attributes:
          label: Summary
          description: A clear and concise summary of the requested feature.
      validations:
          required: true

    - type: textarea
      attributes:
          label: Motivation
          description: |
              Why is this feature important?
              Explain the problem this feature would solve or what use case it would enable.
      validations:
          required: true

    - type: textarea
      attributes:
          label: Proposed Solution
          description: |
              How would you like to see this feature implemented?
              Provide as much detail as possible about the desired behavior, configuration, or changes.
      validations:
          required: true

    - type: textarea
      attributes:
          label: Alternatives Considered
          description: Describe any alternative solutions or workarounds you've thought about.
      validations:
          required: false

    - type: textarea
      attributes:
          label: Additional Context
          description: Add any other context, mockups, or screenshots about the feature request here.
      validations:
          required: false

    - type: markdown
      attributes:
          value: |
              Before submitting, please:
              - Check if there is an existing issue for this feature.
              - Clearly explain the benefit and use case.
              - Be as specific as possible to help contributors evaluate and implement.


================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: [fosrl]


================================================
FILE: .github/ISSUE_TEMPLATE/1.bug_report.yml
================================================
name: Bug Report
description: Create a bug report
labels: []
body:
    - type: textarea
      attributes:
          label: Describe the Bug
          description: A clear and concise description of what the bug is.
      validations:
          required: true

    - type: textarea
      attributes:
          label: Environment
          description: Please fill out the relevant details below for your environment.
          value: |
              - OS Type & Version: (e.g., Ubuntu 22.04)
              - Pangolin Version:
              - Gerbil Version:
              - Traefik Version:
              - Newt Version:
              - Olm Version: (if applicable)
      validations:
          required: true

    - type: textarea
      attributes:
          label: To Reproduce
          description: |
              Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below.

              If using code blocks, make sure syntax highlighting is correct and double-check that the rendered preview is not broken.
      validations:
          required: true

    - type: textarea
      attributes:
          label: Expected Behavior
          description: A clear and concise description of what you expected to happen.
      validations:
          required: true

    - type: markdown
      attributes:
          value: |
              Before posting the issue go through the steps you've written down to make sure the steps provided are detailed and clear.

    - type: markdown
      attributes:
          value: |
              Contributors should be able to follow the steps provided in order to reproduce the bug.


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
    - name: Need help or have questions?
      url: https://github.com/orgs/fosrl/discussions
      about: Ask questions, get help, and discuss with other community members
    - name: Request a Feature
      url: https://github.com/orgs/fosrl/discussions/new?category=feature-requests
      about: Feature requests should be opened as discussions so others can upvote and comment


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
## Community Contribution License Agreement
By creating this pull request, I grant the project maintainers an unlimited,
perpetual license to use, modify, and redistribute these contributions under any terms they
choose, including both the AGPLv3 and the Fossorial Commercial license terms. I
represent that I have the right to grant this license for all contributed content.

## Description


## How to test?



================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "daily"
    groups:
      dev-patch-updates:
        dependency-type: "development"
        update-types:
          - "patch"
      dev-minor-updates:
        dependency-type: "development"
        update-types:
          - "minor"
      prod-patch-updates:
        dependency-type: "production"
        update-types:
          - "patch"
      prod-minor-updates:
        dependency-type: "production"
        update-types:
          - "minor"
          
  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "daily"
    groups:
      patch-updates:
        update-types:
          - "patch"
      minor-updates:
        update-types:
          - "minor"

  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"

  - package-ecosystem: "gomod"
    directory: "/install"
    schedule:
      interval: "daily"
    groups:
      patch-updates:
        update-types:
          - "patch"
      minor-updates:
        update-types:
          - "minor"


================================================
FILE: .github/workflows/cicd.yml
================================================
name: Public CICD Pipeline

# CI/CD workflow for building, publishing, mirroring, signing container images and building release binaries.
# Actions are pinned to specific SHAs to reduce supply-chain risk. This workflow triggers on tag push events.

permissions:
  contents: read
  packages: write      # for GHCR push
  id-token: write      # for Cosign Keyless (OIDC) Signing

# Required secrets:
# - DOCKER_HUB_USERNAME / DOCKER_HUB_ACCESS_TOKEN: push to Docker Hub
# - GITHUB_TOKEN: used for GHCR login and OIDC keyless signing
# - COSIGN_PRIVATE_KEY / COSIGN_PASSWORD / COSIGN_PUBLIC_KEY: for key-based signing

on:
    push:
        tags:
            - "[0-9]+.[0-9]+.[0-9]+"
            - "[0-9]+.[0-9]+.[0-9]+-rc.[0-9]+"

concurrency:
  group: ${{ github.ref }}
  cancel-in-progress: true

jobs:
    pre-run:
        runs-on: ubuntu-latest
        permissions: write-all
        steps:
            - name: Configure AWS credentials
              uses: aws-actions/configure-aws-credentials@v6
              with:
                  role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
                  role-duration-seconds: 3600
                  aws-region: ${{ secrets.AWS_REGION }}

            - name: Verify AWS identity
              run: aws sts get-caller-identity

            - name: Start EC2 instances
              run: |
                  aws ec2 start-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_ARM_RUNNER }}
                  aws ec2 start-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_AMD_RUNNER }}
                  echo "EC2 instances started"


    release-arm:
        name: Build and Release (ARM64)
        runs-on: [self-hosted, linux, arm64, us-east-1]
        needs: [pre-run]
        if: >-
            ${{
                needs.pre-run.result == 'success'
            }}
        # Job-level timeout to avoid runaway or stuck runs
        timeout-minutes: 120
        env:
          # Target images
          DOCKERHUB_IMAGE: docker.io/fosrl/${{ github.event.repository.name }}
          GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}

        steps:
            - name: Checkout code
              uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

            - name: Monitor storage space
              run: |
                  THRESHOLD=75
                  USED_SPACE=$(df / | grep / | awk '{ print $5 }' | sed 's/%//g')
                  echo "Used space: $USED_SPACE%"
                  if [ "$USED_SPACE" -ge "$THRESHOLD" ]; then
                      echo "Used space is below the threshold of 75% free. Running Docker system prune."
                      echo y | docker system prune -a
                  else
                      echo "Storage space is above the threshold. No action needed."
                  fi

            - name: Log in to Docker Hub
              uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
              with:
                  registry: docker.io
                  username: ${{ secrets.DOCKER_HUB_USERNAME }}
                  password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}

            - name: Extract tag name
              id: get-tag
              run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
              shell: bash

            - name: Update version in package.json
              run: |
                  TAG=${{ env.TAG }}
                  sed -i "s/export const APP_VERSION = \".*\";/export const APP_VERSION = \"$TAG\";/" server/lib/consts.ts
                  cat server/lib/consts.ts
              shell: bash

            - name: Check if release candidate
              id: check-rc
              run: |
                  TAG=${{ env.TAG }}
                  if [[ "$TAG" == *"-rc."* ]]; then
                      echo "IS_RC=true" >> $GITHUB_ENV
                  else
                      echo "IS_RC=false" >> $GITHUB_ENV
                  fi
              shell: bash

            - name: Build and push Docker images (Docker Hub - ARM64)
              run: |
                  TAG=${{ env.TAG }}
                  if [ "$IS_RC" = "true" ]; then
                      make build-rc-arm tag=$TAG
                  else
                      make build-release-arm tag=$TAG
                  fi
                  echo "Built & pushed ARM64 images to: ${{ env.DOCKERHUB_IMAGE }}:${TAG}"
              shell: bash

    release-amd:
        name: Build and Release (AMD64)
        runs-on: [self-hosted, linux, x64, us-east-1]
        needs: [pre-run]
        if: >-
            ${{
                needs.pre-run.result == 'success'
            }}
        # Job-level timeout to avoid runaway or stuck runs
        timeout-minutes: 120
        env:
          # Target images
          DOCKERHUB_IMAGE: docker.io/fosrl/${{ github.event.repository.name }}
          GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}

        steps:
            - name: Checkout code
              uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

            - name: Monitor storage space
              run: |
                  THRESHOLD=75
                  USED_SPACE=$(df / | grep / | awk '{ print $5 }' | sed 's/%//g')
                  echo "Used space: $USED_SPACE%"
                  if [ "$USED_SPACE" -ge "$THRESHOLD" ]; then
                      echo "Used space is below the threshold of 75% free. Running Docker system prune."
                      echo y | docker system prune -a
                  else
                      echo "Storage space is above the threshold. No action needed."
                  fi

            - name: Log in to Docker Hub
              uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
              with:
                  registry: docker.io
                  username: ${{ secrets.DOCKER_HUB_USERNAME }}
                  password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}

            - name: Extract tag name
              id: get-tag
              run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
              shell: bash

            - name: Update version in package.json
              run: |
                  TAG=${{ env.TAG }}
                  sed -i "s/export const APP_VERSION = \".*\";/export const APP_VERSION = \"$TAG\";/" server/lib/consts.ts
                  cat server/lib/consts.ts
              shell: bash

            - name: Check if release candidate
              id: check-rc
              run: |
                  TAG=${{ env.TAG }}
                  if [[ "$TAG" == *"-rc."* ]]; then
                      echo "IS_RC=true" >> $GITHUB_ENV
                  else
                      echo "IS_RC=false" >> $GITHUB_ENV
                  fi
              shell: bash

            - name: Build and push Docker images (Docker Hub - AMD64)
              run: |
                  TAG=${{ env.TAG }}
                  if [ "$IS_RC" = "true" ]; then
                      make build-rc-amd tag=$TAG
                  else
                      make build-release-amd tag=$TAG
                  fi
                  echo "Built & pushed AMD64 images to: ${{ env.DOCKERHUB_IMAGE }}:${TAG}"
              shell: bash

    create-manifest:
        name: Create Multi-Arch Manifests
        runs-on: [self-hosted, linux, x64, us-east-1]
        needs: [release-arm, release-amd]
        if: >-
            ${{
                needs.release-arm.result == 'success' &&
                needs.release-amd.result == 'success'
            }}
        timeout-minutes: 30
        steps:
            - name: Checkout code
              uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

            - name: Log in to Docker Hub
              uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
              with:
                  registry: docker.io
                  username: ${{ secrets.DOCKER_HUB_USERNAME }}
                  password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}

            - name: Extract tag name
              id: get-tag
              run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
              shell: bash

            - name: Check if release candidate
              id: check-rc
              run: |
                  TAG=${{ env.TAG }}
                  if [[ "$TAG" == *"-rc."* ]]; then
                      echo "IS_RC=true" >> $GITHUB_ENV
                  else
                      echo "IS_RC=false" >> $GITHUB_ENV
                  fi
              shell: bash

            - name: Create multi-arch manifests
              run: |
                  TAG=${{ env.TAG }}
                  if [ "$IS_RC" = "true" ]; then
                      make create-manifests-rc tag=$TAG
                  else
                      make create-manifests tag=$TAG
                  fi
                  echo "Created multi-arch manifests for tag: ${TAG}"
              shell: bash

    sign-and-package:
        name: Sign and Package
        runs-on: [self-hosted, linux, x64, us-east-1]
        needs: [release-arm, release-amd, create-manifest]
        if: >-
            ${{
                needs.release-arm.result == 'success' &&
                needs.release-amd.result == 'success' &&
                needs.create-manifest.result == 'success'
            }}
        # Job-level timeout to avoid runaway or stuck runs
        timeout-minutes: 120
        env:
          # Target images
          DOCKERHUB_IMAGE: docker.io/fosrl/${{ github.event.repository.name }}
          GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}

        steps:
            - name: Checkout code
              uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

            - name: Extract tag name
              id: get-tag
              run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
              shell: bash

            - name: Install Go
              uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
              with:
                  go-version: 1.24

            - name: Update version in package.json
              run: |
                  TAG=${{ env.TAG }}
                  sed -i "s/export const APP_VERSION = \".*\";/export const APP_VERSION = \"$TAG\";/" server/lib/consts.ts
                  cat server/lib/consts.ts
              shell: bash

            - name: Pull latest Gerbil version
              id: get-gerbil-tag
              run: |
                  LATEST_TAG=$(curl -s https://api.github.com/repos/fosrl/gerbil/tags | jq -r '.[0].name')
                  echo "LATEST_GERBIL_TAG=$LATEST_TAG" >> $GITHUB_ENV
              shell: bash

            - name: Pull latest Badger version
              id: get-badger-tag
              run: |
                  LATEST_TAG=$(curl -s https://api.github.com/repos/fosrl/badger/tags | jq -r '.[0].name')
                  echo "LATEST_BADGER_TAG=$LATEST_TAG" >> $GITHUB_ENV
              shell: bash

            - name: Build installer
              working-directory: install
              run: |
                  make go-build-release \
                    PANGOLIN_VERSION=${{ env.TAG }} \
                    GERBIL_VERSION=${{ env.LATEST_GERBIL_TAG }} \
                    BADGER_VERSION=${{ env.LATEST_BADGER_TAG }}
              shell: bash

            - name: Upload artifacts from /install/bin
              uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
              with:
                  name: install-bin
                  path: install/bin/

            - name: Install skopeo + jq
              # skopeo: copy/inspect images between registries
              # jq: JSON parsing tool used to extract digest values
              run: |
                  sudo apt-get update -y
                  sudo apt-get install -y skopeo jq
                  skopeo --version
              shell: bash

            - name: Login to GHCR
              env:
                  REGISTRY_AUTH_FILE: ${{ runner.temp }}/containers/auth.json
              run: |
                  mkdir -p "$(dirname "$REGISTRY_AUTH_FILE")"
                  skopeo login ghcr.io -u "${{ github.actor }}" -p "${{ secrets.GITHUB_TOKEN }}"
              shell: bash

            - name: Copy tags from Docker Hub to GHCR
              # Mirror the already-built images (all architectures) to GHCR so we can sign them
              # Wait a bit for both architectures to be available in Docker Hub manifest
              env:
                  REGISTRY_AUTH_FILE: ${{ runner.temp }}/containers/auth.json
              run: |
                  set -euo pipefail
                  TAG=${{ env.TAG }}
                  MAJOR_TAG=$(echo $TAG | cut -d. -f1)
                  MINOR_TAG=$(echo $TAG | cut -d. -f1,2)

                  echo "Waiting for multi-arch manifests to be ready..."
                  sleep 30

                  # Determine if this is an RC release
                  IS_RC="false"
                  if [[ "$TAG" == *"-rc."* ]]; then
                      IS_RC="true"
                  fi

                  if [ "$IS_RC" = "true" ]; then
                      echo "RC release detected - copying version-specific tags only"

                      # SQLite OSS
                      echo "Copying ${{ env.DOCKERHUB_IMAGE }}:${TAG} -> ${{ env.GHCR_IMAGE }}:${TAG}"
                      skopeo copy --all --retry-times 3 \
                        docker://$DOCKERHUB_IMAGE:$TAG \
                        docker://$GHCR_IMAGE:$TAG

                      # PostgreSQL OSS
                      echo "Copying ${{ env.DOCKERHUB_IMAGE }}:postgresql-${TAG} -> ${{ env.GHCR_IMAGE }}:postgresql-${TAG}"
                      skopeo copy --all --retry-times 3 \
                        docker://$DOCKERHUB_IMAGE:postgresql-$TAG \
                        docker://$GHCR_IMAGE:postgresql-$TAG

                      # SQLite Enterprise
                      echo "Copying ${{ env.DOCKERHUB_IMAGE }}:ee-${TAG} -> ${{ env.GHCR_IMAGE }}:ee-${TAG}"
                      skopeo copy --all --retry-times 3 \
                        docker://$DOCKERHUB_IMAGE:ee-$TAG \
                        docker://$GHCR_IMAGE:ee-$TAG

                      # PostgreSQL Enterprise
                      echo "Copying ${{ env.DOCKERHUB_IMAGE }}:ee-postgresql-${TAG} -> ${{ env.GHCR_IMAGE }}:ee-postgresql-${TAG}"
                      skopeo copy --all --retry-times 3 \
                        docker://$DOCKERHUB_IMAGE:ee-postgresql-$TAG \
                        docker://$GHCR_IMAGE:ee-postgresql-$TAG
                  else
                      echo "Regular release detected - copying all tags (latest, major, minor, full version)"

                      # SQLite OSS - all tags
                      for TAG_SUFFIX in "latest" "$MAJOR_TAG" "$MINOR_TAG" "$TAG"; do
                          echo "Copying ${{ env.DOCKERHUB_IMAGE }}:${TAG_SUFFIX} -> ${{ env.GHCR_IMAGE }}:${TAG_SUFFIX}"
                          skopeo copy --all --retry-times 3 \
                            docker://$DOCKERHUB_IMAGE:$TAG_SUFFIX \
                            docker://$GHCR_IMAGE:$TAG_SUFFIX
                      done

                      # PostgreSQL OSS - all tags
                      for TAG_SUFFIX in "latest" "$MAJOR_TAG" "$MINOR_TAG" "$TAG"; do
                          echo "Copying ${{ env.DOCKERHUB_IMAGE }}:postgresql-${TAG_SUFFIX} -> ${{ env.GHCR_IMAGE }}:postgresql-${TAG_SUFFIX}"
                          skopeo copy --all --retry-times 3 \
                            docker://$DOCKERHUB_IMAGE:postgresql-$TAG_SUFFIX \
                            docker://$GHCR_IMAGE:postgresql-$TAG_SUFFIX
                      done

                      # SQLite Enterprise - all tags
                      for TAG_SUFFIX in "latest" "$MAJOR_TAG" "$MINOR_TAG" "$TAG"; do
                          echo "Copying ${{ env.DOCKERHUB_IMAGE }}:ee-${TAG_SUFFIX} -> ${{ env.GHCR_IMAGE }}:ee-${TAG_SUFFIX}"
                          skopeo copy --all --retry-times 3 \
                            docker://$DOCKERHUB_IMAGE:ee-$TAG_SUFFIX \
                            docker://$GHCR_IMAGE:ee-$TAG_SUFFIX
                      done

                      # PostgreSQL Enterprise - all tags
                      for TAG_SUFFIX in "latest" "$MAJOR_TAG" "$MINOR_TAG" "$TAG"; do
                          echo "Copying ${{ env.DOCKERHUB_IMAGE }}:ee-postgresql-${TAG_SUFFIX} -> ${{ env.GHCR_IMAGE }}:ee-postgresql-${TAG_SUFFIX}"
                          skopeo copy --all --retry-times 3 \
                            docker://$DOCKERHUB_IMAGE:ee-postgresql-$TAG_SUFFIX \
                            docker://$GHCR_IMAGE:ee-postgresql-$TAG_SUFFIX
                      done
                  fi

                  echo "All images copied successfully to GHCR!"
              shell: bash

            - name: Login to GitHub Container Registry (for cosign)
              uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
              with:
                registry: ghcr.io
                username: ${{ github.actor }}
                password: ${{ secrets.GITHUB_TOKEN }}

            - name: Install cosign
              # cosign is used to sign and verify container images (key and keyless)
              uses: sigstore/cosign-installer@ba7bc0a3fef59531c69a25acd34668d6d3fe6f22 # v4.1.0

            - name: Dual-sign and verify (GHCR & Docker Hub)
              # Sign each image by digest using keyless (OIDC) and key-based signing,
              # then verify both the public key signature and the keyless OIDC signature.
              env:
                  TAG: ${{ env.TAG }}
                  COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
                  COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
                  COSIGN_PUBLIC_KEY: ${{ secrets.COSIGN_PUBLIC_KEY }}
                  COSIGN_YES: "true"
              run: |
                  set -euo pipefail

                  issuer="https://token.actions.githubusercontent.com"
                  id_regex="^https://github.com/${{ github.repository }}/.+"  # accept this repo (all workflows/refs)

                  # Track failures
                  FAILED_TAGS=()
                  SUCCESSFUL_TAGS=()

                  # Determine if this is an RC release
                  IS_RC="false"
                  if [[ "$TAG" == *"-rc."* ]]; then
                      IS_RC="true"
                  fi

                  # Define image variants to sign
                  if [ "$IS_RC" = "true" ]; then
                      echo "RC release - signing version-specific tags only"
                      IMAGE_TAGS=(
                          "${TAG}"
                          "postgresql-${TAG}"
                          "ee-${TAG}"
                          "ee-postgresql-${TAG}"
                      )
                  else
                      echo "Regular release - signing all tags"
                      MAJOR_TAG=$(echo $TAG | cut -d. -f1)
                      MINOR_TAG=$(echo $TAG | cut -d. -f1,2)
                      IMAGE_TAGS=(
                          "latest" "$MAJOR_TAG" "$MINOR_TAG" "$TAG"
                          "postgresql-latest" "postgresql-$MAJOR_TAG" "postgresql-$MINOR_TAG" "postgresql-$TAG"
                          "ee-latest" "ee-$MAJOR_TAG" "ee-$MINOR_TAG" "ee-$TAG"
                          "ee-postgresql-latest" "ee-postgresql-$MAJOR_TAG" "ee-postgresql-$MINOR_TAG" "ee-postgresql-$TAG"
                      )
                  fi

                  # Sign each image variant for both registries
                  for BASE_IMAGE in "${GHCR_IMAGE}" "${DOCKERHUB_IMAGE}"; do
                    for IMAGE_TAG in "${IMAGE_TAGS[@]}"; do
                      echo "Processing ${BASE_IMAGE}:${IMAGE_TAG}"
                      TAG_FAILED=false

                      # Wrap the entire tag processing in error handling
                      (
                        set -e
                        DIGEST="$(skopeo inspect --retry-times 3 docker://${BASE_IMAGE}:${IMAGE_TAG} | jq -r '.Digest')"
                        REF="${BASE_IMAGE}@${DIGEST}"
                        echo "Resolved digest: ${REF}"

                        echo "==> cosign sign (keyless) --recursive ${REF}"
                        cosign sign --recursive "${REF}"

                        echo "==> cosign sign (key) --recursive ${REF}"
                        cosign sign --key env://COSIGN_PRIVATE_KEY --recursive "${REF}"

                        # Retry wrapper for verification to handle registry propagation delays
                        retry_verify() {
                          local cmd="$1"
                          local attempts=6
                          local delay=5
                          local i=1
                          until eval "$cmd"; do
                            if [ $i -ge $attempts ]; then
                              echo "Verification failed after $attempts attempts"
                              return 1
                            fi
                            echo "Verification not yet available. Retry $i/$attempts after ${delay}s..."
                            sleep $delay
                            i=$((i+1))
                            delay=$((delay*2))
                            # Cap the delay to avoid very long waits
                            if [ $delay -gt 60 ]; then delay=60; fi
                          done
                          return 0
                        }

                        echo "==> cosign verify (public key) ${REF}"
                        if retry_verify "cosign verify --key env://COSIGN_PUBLIC_KEY '${REF}' -o text"; then
                          VERIFIED_INDEX=true
                        else
                          VERIFIED_INDEX=false
                        fi

                        echo "==> cosign verify (keyless policy) ${REF}"
                        if retry_verify "cosign verify --certificate-oidc-issuer '${issuer}' --certificate-identity-regexp '${id_regex}' '${REF}' -o text"; then
                          VERIFIED_INDEX_KEYLESS=true
                        else
                          VERIFIED_INDEX_KEYLESS=false
                        fi

                        # Check if verification succeeded
                        if [ "${VERIFIED_INDEX}" != "true" ] && [ "${VERIFIED_INDEX_KEYLESS}" != "true" ]; then
                          echo "⚠️  WARNING: Verification not available for ${BASE_IMAGE}:${IMAGE_TAG}"
                          echo "This may be due to registry propagation delays. Continuing anyway."
                        fi
                      ) || TAG_FAILED=true

                      if [ "$TAG_FAILED" = "true" ]; then
                        echo "⚠️  WARNING: Failed to sign/verify ${BASE_IMAGE}:${IMAGE_TAG}"
                        FAILED_TAGS+=("${BASE_IMAGE}:${IMAGE_TAG}")
                      else
                        echo "✓ Successfully signed and verified ${BASE_IMAGE}:${IMAGE_TAG}"
                        SUCCESSFUL_TAGS+=("${BASE_IMAGE}:${IMAGE_TAG}")
                      fi
                    done
                  done

                  # Report summary
                  echo ""
                  echo "=========================================="
                  echo "Sign and Verify Summary"
                  echo "=========================================="
                  echo "Successful: ${#SUCCESSFUL_TAGS[@]}"
                  echo "Failed: ${#FAILED_TAGS[@]}"
                  echo ""

                  if [ ${#FAILED_TAGS[@]} -gt 0 ]; then
                    echo "Failed tags:"
                    for tag in "${FAILED_TAGS[@]}"; do
                      echo "  - $tag"
                    done
                    echo ""
                    echo "⚠️  WARNING: Some tags failed to sign/verify, but continuing anyway"
                  else
                    echo "✓ All images signed and verified successfully!"
                  fi
              shell: bash

    post-run:
        needs: [pre-run, release-arm, release-amd, create-manifest, sign-and-package]
        if: >-
            ${{
                always() &&
                needs.pre-run.result == 'success' &&
                (needs.release-arm.result == 'success' || needs.release-arm.result == 'skipped' || needs.release-arm.result == 'failure') &&
                (needs.release-amd.result == 'success' || needs.release-amd.result == 'skipped' || needs.release-amd.result == 'failure') &&
                (needs.create-manifest.result == 'success' || needs.create-manifest.result == 'skipped' || needs.create-manifest.result == 'failure') &&
                (needs.sign-and-package.result == 'success' || needs.sign-and-package.result == 'skipped' || needs.sign-and-package.result == 'failure')
            }}
        runs-on: ubuntu-latest
        permissions: write-all
        steps:
            - name: Configure AWS credentials
              uses: aws-actions/configure-aws-credentials@v6
              with:
                  role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
                  role-duration-seconds: 3600
                  aws-region: ${{ secrets.AWS_REGION }}

            - name: Verify AWS identity
              run: aws sts get-caller-identity

            - name: Stop EC2 instances
              run: |
                  aws ec2 stop-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_ARM_RUNNER }}
                  aws ec2 stop-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_AMD_RUNNER }}
                  echo "EC2 instances stopped"


================================================
FILE: .github/workflows/linting.yml
================================================
name: ESLint

permissions:
  contents: read

on:
    pull_request:
        paths:
            - '**/*.js'
            - '**/*.jsx'
            - '**/*.ts'
            - '**/*.tsx'
            - '.eslintrc*'
            - 'package.json'
            - 'yarn.lock'
            - 'pnpm-lock.yaml'
            - 'package-lock.json'

jobs:
    Linter:
        runs-on: ubuntu-latest
        steps:
            - name: Checkout code
              uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

            - name: Set up Node.js
              uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
              with:
                node-version: '24'

            - name: Install dependencies
              run: npm ci

            - name: Create build file
              run: npm run set:oss

            - name: Run ESLint
              run: npx eslint . --ext .js,.jsx,.ts,.tsx


================================================
FILE: .github/workflows/mirror.yaml
================================================
name: Mirror & Sign (Docker Hub to GHCR)

on:
  workflow_dispatch: {}

permissions:
  contents: read
  packages: write
  id-token: write   # for keyless OIDC

env:
  SOURCE_IMAGE: docker.io/fosrl/pangolin
  DEST_IMAGE: ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}

jobs:
  mirror-and-dual-sign:
    runs-on: amd64-runner
    steps:
      - name: Install skopeo + jq
        run: |
          sudo apt-get update -y
          sudo apt-get install -y skopeo jq
          skopeo --version

      - name: Install cosign
        uses: sigstore/cosign-installer@ba7bc0a3fef59531c69a25acd34668d6d3fe6f22 # v4.1.0

      - name: Input check
        run: |
          test -n "${SOURCE_IMAGE}" || (echo "SOURCE_IMAGE is empty" && exit 1)
          echo "Source : ${SOURCE_IMAGE}"
          echo "Target : ${DEST_IMAGE}"

      # Auth for skopeo (containers-auth)
      - name: Skopeo login to GHCR
        run: |
          skopeo login ghcr.io -u "${{ github.actor }}" -p "${{ secrets.GITHUB_TOKEN }}"

      # Auth for cosign (docker-config)
      - name: Docker login to GHCR (for cosign)
        run: |
          echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin

      - name: List source tags
        run: |
          set -euo pipefail
          skopeo list-tags --retry-times 3 docker://"${SOURCE_IMAGE}" \
            | jq -r '.Tags[]' | grep -v -e '-arm64' -e '-amd64' | sort -u > src-tags.txt
          echo "Found source tags: $(wc -l < src-tags.txt)"
          head -n 20 src-tags.txt || true

      - name: List destination tags (skip existing)
        run: |
          set -euo pipefail
          if skopeo list-tags --retry-times 3 docker://"${DEST_IMAGE}" >/tmp/dst.json 2>/dev/null; then
            jq -r '.Tags[]' /tmp/dst.json | sort -u > dst-tags.txt
          else
            : > dst-tags.txt
          fi
          echo "Existing destination tags: $(wc -l < dst-tags.txt)"

      - name: Mirror, dual-sign, and verify
        env:
          # keyless
          COSIGN_YES: "true"
          # key-based
          COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
          COSIGN_PASSWORD:    ${{ secrets.COSIGN_PASSWORD }}
          # verify
          COSIGN_PUBLIC_KEY:  ${{ secrets.COSIGN_PUBLIC_KEY }}
        run: |
          set -euo pipefail
          copied=0; skipped=0; v_ok=0; errs=0

          issuer="https://token.actions.githubusercontent.com"
          id_regex="^https://github.com/${{ github.repository }}/.+"

          while read -r tag; do
            [ -z "$tag" ] && continue

            if grep -Fxq "$tag" dst-tags.txt; then
              echo "::notice ::Skip (exists) ${DEST_IMAGE}:${tag}"
              skipped=$((skipped+1))
              continue
            fi

            echo "==> Copy ${SOURCE_IMAGE}:${tag} → ${DEST_IMAGE}:${tag}"
            if ! skopeo copy --all --retry-times 3 \
                 docker://"${SOURCE_IMAGE}:${tag}" docker://"${DEST_IMAGE}:${tag}"; then
              echo "::warning title=Copy failed::${SOURCE_IMAGE}:${tag}"
              errs=$((errs+1)); continue
            fi
            copied=$((copied+1))

            digest="$(skopeo inspect --retry-times 3 docker://"${DEST_IMAGE}:${tag}" | jq -r '.Digest')"
            ref="${DEST_IMAGE}@${digest}"

            echo "==> cosign sign (keyless) --recursive ${ref}"
            if ! cosign sign --recursive "${ref}"; then
              echo "::warning title=Keyless sign failed::${ref}"
              errs=$((errs+1))
            fi

            echo "==> cosign sign (key) --recursive ${ref}"
            if ! cosign sign --key env://COSIGN_PRIVATE_KEY --recursive "${ref}"; then
              echo "::warning title=Key sign failed::${ref}"
              errs=$((errs+1))
            fi

            echo "==> cosign verify (public key) ${ref}"
            if ! cosign verify --key env://COSIGN_PUBLIC_KEY "${ref}" -o text; then
              echo "::warning title=Verify(pubkey) failed::${ref}"
              errs=$((errs+1))
            fi

            echo "==> cosign verify (keyless policy) ${ref}"
            if ! cosign verify \
                 --certificate-oidc-issuer "${issuer}" \
                 --certificate-identity-regexp "${id_regex}" \
                 "${ref}" -o text; then
              echo "::warning title=Verify(keyless) failed::${ref}"
              errs=$((errs+1))
            else
              v_ok=$((v_ok+1))
            fi
          done < src-tags.txt

          echo "---- Summary ----"
          echo "Copied        : $copied"
          echo "Skipped       : $skipped"
          echo "Verified OK   : $v_ok"
          echo "Errors        : $errs"


================================================
FILE: .github/workflows/restart-runners.yml
================================================
name: Restart Runners

on:
  schedule:
    - cron: '0 0 */7 * *'

permissions:
    id-token: write
    contents: read

jobs:
  ec2-maintenance-prod:
    runs-on: ubuntu-latest
    permissions: write-all
    steps:
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v6
        with:
          role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
          role-duration-seconds: 3600
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Verify AWS identity
        run: aws sts get-caller-identity

      - name: Start EC2 instance
        run: |
          aws ec2 start-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_ARM_RUNNER }}
          aws ec2 start-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_AMD_RUNNER }}
          echo "EC2 instances started"

      - name: Wait
        run: sleep 600

      - name: Stop EC2 instance
        run: |
          aws ec2 stop-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_ARM_RUNNER }}
          aws ec2 stop-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_AMD_RUNNER }}
          echo "EC2 instances stopped"


================================================
FILE: .github/workflows/saas.yml
================================================
name: SAAS Pipeline

# CI/CD workflow for building, publishing, mirroring, signing container images and building release binaries.
# Actions are pinned to specific SHAs to reduce supply-chain risk. This workflow triggers on tag push events.

permissions:
  contents: read
  packages: write      # for GHCR push
  id-token: write      # for Cosign Keyless (OIDC) Signing

on:
    push:
        tags:
            - "[0-9]+.[0-9]+.[0-9]+-s.[0-9]+"

concurrency:
  group: ${{ github.ref }}
  cancel-in-progress: true

jobs:
    pre-run:
        runs-on: ubuntu-latest
        permissions: write-all
        steps:
            - name: Configure AWS credentials
              uses: aws-actions/configure-aws-credentials@v6
              with:
                  role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
                  role-duration-seconds: 3600
                  aws-region: ${{ secrets.AWS_REGION }}

            - name: Verify AWS identity
              run: aws sts get-caller-identity

            - name: Start EC2 instances
              run: |
                  aws ec2 start-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_ARM_RUNNER }}
                  echo "EC2 instances started"


    release-arm:
        name: Build and Release (ARM64)
        runs-on: [self-hosted, linux, arm64, us-east-1]
        needs: [pre-run]
        if: >-
            ${{
                needs.pre-run.result == 'success'
            }}
        # Job-level timeout to avoid runaway or stuck runs
        timeout-minutes: 120
        env:
          # Target images
          AWS_IMAGE: ${{ secrets.aws_account_id }}.dkr.ecr.us-east-1.amazonaws.com/${{ github.event.repository.name }}

        steps:
            - name: Checkout code
              uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

            - name: Download MaxMind GeoLite2 databases
              env:
                MAXMIND_LICENSE_KEY: ${{ secrets.MAXMIND_LICENSE_KEY }}
              run: |
                echo "Downloading MaxMind GeoLite2 databases..."

                # Download GeoLite2-Country
                curl -L "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=${MAXMIND_LICENSE_KEY}&suffix=tar.gz" \
                  -o GeoLite2-Country.tar.gz

                # Download GeoLite2-ASN
                curl -L "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-ASN&license_key=${MAXMIND_LICENSE_KEY}&suffix=tar.gz" \
                  -o GeoLite2-ASN.tar.gz

                # Extract the .mmdb files
                tar -xzf GeoLite2-Country.tar.gz --strip-components=1 --wildcards '*.mmdb'
                tar -xzf GeoLite2-ASN.tar.gz --strip-components=1 --wildcards '*.mmdb'

                # Verify files exist
                if [ ! -f "GeoLite2-Country.mmdb" ]; then
                  echo "ERROR: Failed to download GeoLite2-Country.mmdb"
                  exit 1
                fi

                if [ ! -f "GeoLite2-ASN.mmdb" ]; then
                  echo "ERROR: Failed to download GeoLite2-ASN.mmdb"
                  exit 1
                fi

                # Clean up tar files
                rm -f GeoLite2-Country.tar.gz GeoLite2-ASN.tar.gz

                echo "MaxMind databases downloaded successfully"
                ls -lh GeoLite2-*.mmdb

            - name: Monitor storage space
              run: |
                  THRESHOLD=75
                  USED_SPACE=$(df / | grep / | awk '{ print $5 }' | sed 's/%//g')
                  echo "Used space: $USED_SPACE%"
                  if [ "$USED_SPACE" -ge "$THRESHOLD" ]; then
                      echo "Used space is below the threshold of 75% free. Running Docker system prune."
                      echo y | docker system prune -a
                  else
                      echo "Storage space is above the threshold. No action needed."
                  fi

            - name: Configure AWS credentials
              uses: aws-actions/configure-aws-credentials@v6
              with:
                  role-to-assume: arn:aws:iam::${{ secrets.aws_account_id }}:role/${{ secrets.AWS_ROLE_NAME }}
                  role-duration-seconds: 3600
                  aws-region: ${{ secrets.AWS_REGION }}

            - name: Login to Amazon ECR
              id: login-ecr
              uses: aws-actions/amazon-ecr-login@v2

            - name: Extract tag name
              id: get-tag
              run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
              shell: bash

            - name: Update version in package.json
              run: |
                  TAG=${{ env.TAG }}
                  sed -i "s/export const APP_VERSION = \".*\";/export const APP_VERSION = \"$TAG\";/" server/lib/consts.ts
                  cat server/lib/consts.ts
              shell: bash

            - name: Build and push Docker images (Docker Hub - ARM64)
              run: |
                  TAG=${{ env.TAG }}
                  make build-saas tag=$TAG
                  echo "Built & pushed ARM64 images to: ${{ env.AWS_IMAGE }}:${TAG}"
              shell: bash

    post-run:
        needs: [pre-run, release-arm]
        if: >-
            ${{
                always() &&
                needs.pre-run.result == 'success' &&
                (needs.release-arm.result == 'success' || needs.release-arm.result == 'skipped' || needs.release-arm.result == 'failure')
            }}
        runs-on: ubuntu-latest
        permissions: write-all
        steps:
            - name: Configure AWS credentials
              uses: aws-actions/configure-aws-credentials@v6
              with:
                  role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
                  role-duration-seconds: 3600
                  aws-region: ${{ secrets.AWS_REGION }}

            - name: Verify AWS identity
              run: aws sts get-caller-identity

            - name: Stop EC2 instances
              run: |
                  aws ec2 stop-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_ARM_RUNNER }}
                  echo "EC2 instances stopped"


================================================
FILE: .github/workflows/stale-bot.yml
================================================
name: Mark and Close Stale Issues

on:
  schedule:
    - cron: '0 0 * * *'
  workflow_dispatch: # Allow manual trigger

permissions:
  contents: write # only for delete-branch option
  issues: write
  pull-requests: write

jobs:
  stale:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
        with:
          days-before-stale: 14
          days-before-close: 14
          stale-issue-message: 'This issue has been automatically marked as stale due to 14 days of inactivity. It will be closed in 14 days if no further activity occurs.'
          close-issue-message: 'This issue has been automatically closed due to inactivity. If you believe this is still relevant, please open a new issue with up-to-date information.'
          stale-issue-label: 'stale'
          
          exempt-issue-labels: 'needs investigating, networking, new feature, reverse proxy, bug, api, authentication, documentation, enhancement, help wanted, good first issue, question'
          
          exempt-all-issue-assignees: true
          
          only-labels: ''
          exempt-pr-labels: ''
          days-before-pr-stale: -1
          days-before-pr-close: -1
          
          operations-per-run: 100
          remove-stale-when-updated: true
          delete-branch: false
          enable-statistics: true


================================================
FILE: .github/workflows/test.yml
================================================
name: Run Tests

permissions:
  contents: read

on:
  pull_request:
    branches:
      - main
      - dev

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Install Node
        uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
        with:
          node-version: '24'

      - name: Copy config file
        run: cp config/config.example.yml config/config.yml

      - name: Install dependencies
        run: npm ci

      - name: Create database index.ts
        run: npm run set:sqlite

      - name: Create build file
        run: npm run set:oss

      - name: Generate database migrations
        run: npm run db:generate

      - name: Apply database migrations
        run: npm run db:push

      - name: Test with tsc
        run: npx tsc --noEmit

      - name: Start app in background
        run: nohup npm run dev &

      - name: Wait for app availability
        run: |
          for i in {1..5}; do
            if curl --silent --fail http://localhost:3002/auth/login; then
              echo "App is up"
              exit 0
            fi
            echo "Waiting for the app... attempt $i"
            sleep 5
          done
          echo "App failed to start"
          exit 1

  build-sqlite:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Build Docker image sqlite
        run: make dev-build-sqlite

  build-postgres:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Build Docker image pg
        run: make dev-build-pg


================================================
FILE: .gitignore
================================================
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
/coverage
/.next/
/out/
/build
.DS_Store
*.pem
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.env*.local
.env
.vercel
*.tsbuildinfo
next-env.d.ts
*.db
*.sqlite
!Dockerfile.sqlite
*.sqlite3
*.log
.machinelogs*.json
*-audit.json
migrations
tsconfig.tsbuildinfo
config/config.yml
config/config.saas.yml
config/config.oss.yml
config/config.enterprise.yml
config/privateConfig.yml
config/postgres
config/postgres*
config/openapi.yaml
config/key
dist
.dist
installer
*.tar
bin
.secrets
test_event.json
.idea/
public/branding
server/db/index.ts
server/build.ts
postgres/
dynamic/
*.mmdb
scratch/
tsconfig.json
hydrateSaas.ts
CLAUDE.md
drizzle.config.ts
server/setup/migrations.ts


================================================
FILE: .nvmrc
================================================
24


================================================
FILE: .prettierignore
================================================
.github/
bruno/
cli/
config/
messages/
next.config.mjs/
public/
tailwind.config.js/
test/
**/*.yml
**/*.yaml
**/*.md

================================================
FILE: .prettierrc
================================================
{
    "tabWidth": 4,
    "printWidth": 80,
    "trailingComma": "none"
}


================================================
FILE: .vscode/extensions.json
================================================
{
    "recommendations": ["esbenp.prettier-vscode"]
}


================================================
FILE: .vscode/settings.json
================================================
{
    "editor.codeActionsOnSave": {
        "source.addMissingImports.ts": "always"
    },
    "editor.defaultFormatter": "esbenp.prettier-vscode",
    "[jsonc]": {
        "editor.defaultFormatter": "vscode.json-language-features"
    },
    "[javascript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
    "[typescript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
    "[typescriptreact]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
    "[json]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
    "editor.formatOnSave": true
}

================================================
FILE: CONTRIBUTING.md
================================================
## Contributing

Contributions are welcome! 

Please see the contribution and local development guide on the docs page before getting started:

https://docs.pangolin.net/development/contributing

### Licensing Considerations

Please note that your contributions will be distributed under the AGPLv3 and the Fossorial Commercial license. For inquiries about commercial licensing, please contact us.

At the beginning of all pull requests please place the following contributor license agreement (CLA). For larger submissions, we may reach out with a more formal agreement.

```
By creating this pull request, I grant the project maintainers an unlimited,
perpetual license to use, modify, and redistribute these contributions under any terms they
choose, including both the AGPLv3 and the Fossorial Commercial license terms. I
represent that I have the right to grant this license for all contributed content.
```


================================================
FILE: Dockerfile
================================================
# FROM node:24-slim AS base
FROM public.ecr.aws/docker/library/node:24-slim AS base

WORKDIR /app

RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/*

COPY package*.json ./

FROM base AS builder-dev

RUN npm ci

COPY . .

ARG BUILD=oss
ARG DATABASE=sqlite

RUN if [ "$BUILD" = "oss" ]; then rm -rf server/private; fi && \
    npm run set:$DATABASE && \
    npm run set:$BUILD && \
    npm run db:generate && \
    npm run build && \
    npm run build:cli && \
    test -f dist/server.mjs

# Create placeholder files for MaxMind databases to avoid COPY errors
# Real files should be present for saas builds, placeholders for oss builds
RUN touch /app/GeoLite2-Country.mmdb /app/GeoLite2-ASN.mmdb

FROM base AS builder

RUN npm ci --omit=dev

# FROM node:24-slim AS runner
FROM public.ecr.aws/docker/library/node:24-slim AS runner

WORKDIR /app

RUN apt-get update && apt-get install -y curl tzdata && rm -rf /var/lib/apt/lists/*

COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json

COPY --from=builder-dev /app/.next/standalone ./
COPY --from=builder-dev /app/.next/static ./.next/static
COPY --from=builder-dev /app/dist ./dist
COPY --from=builder-dev /app/server/migrations ./dist/init

COPY ./cli/wrapper.sh /usr/local/bin/pangctl
RUN chmod +x /usr/local/bin/pangctl ./dist/cli.mjs

COPY server/db/names.json ./dist/names.json
COPY server/db/ios_models.json ./dist/ios_models.json
COPY server/db/mac_models.json ./dist/mac_models.json
COPY public ./public

# Copy MaxMind databases for SaaS builds
ARG BUILD=oss

RUN mkdir -p ./maxmind

# Copy MaxMind databases (placeholders exist for oss builds, real files for saas)
COPY --from=builder-dev /app/GeoLite2-Country.mmdb ./maxmind/GeoLite2-Country.mmdb
COPY --from=builder-dev /app/GeoLite2-ASN.mmdb ./maxmind/GeoLite2-ASN.mmdb

# Remove MaxMind databases for non-saas builds (keep only for saas)
RUN if [ "$BUILD" != "saas" ]; then rm -rf ./maxmind; fi

# OCI Image Labels - Build Args for dynamic values
ARG VERSION="dev"
ARG REVISION=""
ARG CREATED=""
ARG LICENSE="AGPL-3.0"

# Derive title and description based on BUILD type
ARG IMAGE_TITLE="Pangolin"
ARG IMAGE_DESCRIPTION="Identity-aware VPN and proxy for remote access to anything, anywhere"

# OCI Image Labels
# https://github.com/opencontainers/image-spec/blob/main/annotations.md
LABEL org.opencontainers.image.source="https://github.com/fosrl/pangolin" \
      org.opencontainers.image.url="https://github.com/fosrl/pangolin" \
      org.opencontainers.image.documentation="https://docs.pangolin.net" \
      org.opencontainers.image.vendor="Fossorial" \
      org.opencontainers.image.licenses="${LICENSE}" \
      org.opencontainers.image.title="${IMAGE_TITLE}" \
      org.opencontainers.image.description="${IMAGE_DESCRIPTION}" \
      org.opencontainers.image.version="${VERSION}" \
      org.opencontainers.image.revision="${REVISION}" \
      org.opencontainers.image.created="${CREATED}"

CMD ["npm", "run", "start"]


================================================
FILE: Dockerfile.dev
================================================
FROM node:24-alpine

WORKDIR /app

RUN apk add --no-cache python3 make g++

COPY package*.json ./

# Install dependencies
RUN npm ci

# Copy source code
COPY . .

# Use tsx watch for development with hot reload
CMD ["npm", "run", "dev"]


================================================
FILE: LICENSE
================================================
Copyright (c) 2025 Fossorial, Inc.

Portions of this software are licensed as follows:

* All files that include a header specifying they are licensed under the
  "Fossorial Commercial License" are governed by the Fossorial Commercial
  License terms. The specific terms applicable to each customer depend on the
  commercial license tier agreed upon in writing with Fossorial, Inc.
  Unauthorized use, copying, modification, or distribution is strictly
  prohibited.

* All files that include a header specifying they are licensed under the GNU
  Affero General Public License, Version 3 ("AGPL-3"), are governed by the
  AGPL-3 terms. A full copy of the AGPL-3 license is provided below. However,
  these files are also available under the Fossorial Commercial License if a
  separate commercial license agreement has been executed between the customer
  and Fossorial, Inc.

* All files without a license header are, by default, licensed under the GNU
  Affero General Public License, Version 3 (AGPL-3). These files may also be
  made available under the Fossorial Commercial License upon agreement with
  Fossorial, Inc.

* All third-party components included in this repository are licensed under
  their respective original licenses, as provided by their authors.

Please consult the header of each individual file to determine the applicable
license. For AGPL-3 licensed files, dual-licensing under the Fossorial
Commercial License is available subject to written agreement with Fossorial,
Inc.

                    GNU AFFERO GENERAL PUBLIC LICENSE
                       Version 3, 19 November 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.

  A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate.  Many developers of free software are heartened and
encouraged by the resulting cooperation.  However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.

  The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community.  It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server.  Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.

  An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals.  This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU Affero General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Remote Network Interaction; Use with the GNU General Public License.

  Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software.  This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time.  Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source.  For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code.  There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.


================================================
FILE: Makefile
================================================
.PHONY: build build-pg build-release build-release-arm build-release-amd create-manifests build-arm build-x86 test clean

major_tag := $(shell echo $(tag) | cut -d. -f1)
minor_tag := $(shell echo $(tag) | cut -d. -f1,2)

# OCI label variables
CREATED := $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
REVISION := $(shell git rev-parse HEAD 2>/dev/null || echo "unknown")

# Common OCI build args for OSS builds
OCI_ARGS_OSS = --build-arg VERSION=$(tag) \
	--build-arg REVISION=$(REVISION) \
	--build-arg CREATED=$(CREATED) \
	--build-arg IMAGE_TITLE="Pangolin" \
	--build-arg IMAGE_DESCRIPTION="Identity-aware VPN and proxy for remote access to anything, anywhere"

# Common OCI build args for Enterprise builds
OCI_ARGS_EE = --build-arg VERSION=$(tag) \
	--build-arg REVISION=$(REVISION) \
	--build-arg CREATED=$(CREATED) \
	--build-arg LICENSE="Fossorial Commercial" \
	--build-arg IMAGE_TITLE="Pangolin EE" \
	--build-arg IMAGE_DESCRIPTION="Pangolin Enterprise Edition - Identity-aware VPN and proxy for remote access to anything, anywhere"

.PHONY: build-release build-sqlite build-postgresql build-ee-sqlite build-ee-postgresql

build-release: build-sqlite build-postgresql build-ee-sqlite build-ee-postgresql

build-sqlite:
	@if [ -z "$(tag)" ]; then \
		echo "Error: tag is required. Usage: make build-release tag=<tag>"; \
		exit 1; \
	fi
	docker buildx build \
		--build-arg BUILD=oss \
		--build-arg DATABASE=sqlite \
		$(OCI_ARGS_OSS) \
		--platform linux/arm64,linux/amd64 \
		--tag fosrl/pangolin:latest \
		--tag fosrl/pangolin:$(major_tag) \
		--tag fosrl/pangolin:$(minor_tag) \
		--tag fosrl/pangolin:$(tag) \
		--push .

build-postgresql:
	@if [ -z "$(tag)" ]; then \
		echo "Error: tag is required. Usage: make build-release tag=<tag>"; \
		exit 1; \
	fi
	docker buildx build \
		--build-arg BUILD=oss \
		--build-arg DATABASE=pg \
		$(OCI_ARGS_OSS) \
		--platform linux/arm64,linux/amd64 \
		--tag fosrl/pangolin:postgresql-latest \
		--tag fosrl/pangolin:postgresql-$(major_tag) \
		--tag fosrl/pangolin:postgresql-$(minor_tag) \
		--tag fosrl/pangolin:postgresql-$(tag) \
		--push .

build-ee-sqlite:
	@if [ -z "$(tag)" ]; then \
		echo "Error: tag is required. Usage: make build-release tag=<tag>"; \
		exit 1; \
	fi
	docker buildx build \
		--build-arg BUILD=enterprise \
		--build-arg DATABASE=sqlite \
		$(OCI_ARGS_EE) \
		--platform linux/arm64,linux/amd64 \
		--tag fosrl/pangolin:ee-latest \
		--tag fosrl/pangolin:ee-$(major_tag) \
		--tag fosrl/pangolin:ee-$(minor_tag) \
		--tag fosrl/pangolin:ee-$(tag) \
		--push .

build-ee-postgresql:
	@if [ -z "$(tag)" ]; then \
		echo "Error: tag is required. Usage: make build-release tag=<tag>"; \
		exit 1; \
	fi
	docker buildx build \
		--build-arg BUILD=enterprise \
		--build-arg DATABASE=pg \
		$(OCI_ARGS_EE) \
		--platform linux/arm64,linux/amd64 \
		--tag fosrl/pangolin:ee-postgresql-latest \
		--tag fosrl/pangolin:ee-postgresql-$(major_tag) \
		--tag fosrl/pangolin:ee-postgresql-$(minor_tag) \
		--tag fosrl/pangolin:ee-postgresql-$(tag) \
		--push .

build-saas:
	@if [ -z "$(tag)" ]; then \
		echo "Error: tag is required. Usage: make build-release tag=<tag>"; \
		exit 1; \
	fi
	docker buildx build \
		--build-arg BUILD=saas \
		--build-arg DATABASE=pg \
		--platform linux/arm64 \
		--tag $(AWS_IMAGE):$(tag) \
		--push .

build-release-arm:
	@if [ -z "$(tag)" ]; then \
		echo "Error: tag is required. Usage: make build-release-arm tag=<tag>"; \
		exit 1; \
	fi
	@MAJOR_TAG=$$(echo $(tag) | cut -d. -f1); \
	MINOR_TAG=$$(echo $(tag) | cut -d. -f1,2); \
	CREATED=$$(date -u +"%Y-%m-%dT%H:%M:%SZ"); \
	REVISION=$$(git rev-parse HEAD 2>/dev/null || echo "unknown"); \
	docker buildx build \
		--build-arg BUILD=oss \
		--build-arg DATABASE=sqlite \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg IMAGE_TITLE="Pangolin" \
		--build-arg IMAGE_DESCRIPTION="Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/arm64 \
		--tag fosrl/pangolin:latest-arm64 \
		--tag fosrl/pangolin:$$MAJOR_TAG-arm64 \
		--tag fosrl/pangolin:$$MINOR_TAG-arm64 \
		--tag fosrl/pangolin:$(tag)-arm64 \
		--push . && \
	docker buildx build \
		--build-arg BUILD=oss \
		--build-arg DATABASE=pg \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg IMAGE_TITLE="Pangolin" \
		--build-arg IMAGE_DESCRIPTION="Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/arm64 \
		--tag fosrl/pangolin:postgresql-latest-arm64 \
		--tag fosrl/pangolin:postgresql-$$MAJOR_TAG-arm64 \
		--tag fosrl/pangolin:postgresql-$$MINOR_TAG-arm64 \
		--tag fosrl/pangolin:postgresql-$(tag)-arm64 \
		--push . && \
	docker buildx build \
		--build-arg BUILD=enterprise \
		--build-arg DATABASE=sqlite \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg LICENSE="Fossorial Commercial" \
		--build-arg IMAGE_TITLE="Pangolin EE" \
		--build-arg IMAGE_DESCRIPTION="Pangolin Enterprise Edition - Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/arm64 \
		--tag fosrl/pangolin:ee-latest-arm64 \
		--tag fosrl/pangolin:ee-$$MAJOR_TAG-arm64 \
		--tag fosrl/pangolin:ee-$$MINOR_TAG-arm64 \
		--tag fosrl/pangolin:ee-$(tag)-arm64 \
		--push . && \
	docker buildx build \
		--build-arg BUILD=enterprise \
		--build-arg DATABASE=pg \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg LICENSE="Fossorial Commercial" \
		--build-arg IMAGE_TITLE="Pangolin EE" \
		--build-arg IMAGE_DESCRIPTION="Pangolin Enterprise Edition - Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/arm64 \
		--tag fosrl/pangolin:ee-postgresql-latest-arm64 \
		--tag fosrl/pangolin:ee-postgresql-$$MAJOR_TAG-arm64 \
		--tag fosrl/pangolin:ee-postgresql-$$MINOR_TAG-arm64 \
		--tag fosrl/pangolin:ee-postgresql-$(tag)-arm64 \
		--push .

build-release-amd:
	@if [ -z "$(tag)" ]; then \
		echo "Error: tag is required. Usage: make build-release-amd tag=<tag>"; \
		exit 1; \
	fi
	@MAJOR_TAG=$$(echo $(tag) | cut -d. -f1); \
	MINOR_TAG=$$(echo $(tag) | cut -d. -f1,2); \
	CREATED=$$(date -u +"%Y-%m-%dT%H:%M:%SZ"); \
	REVISION=$$(git rev-parse HEAD 2>/dev/null || echo "unknown"); \
	docker buildx build \
		--build-arg BUILD=oss \
		--build-arg DATABASE=sqlite \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg IMAGE_TITLE="Pangolin" \
		--build-arg IMAGE_DESCRIPTION="Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/amd64 \
		--tag fosrl/pangolin:latest-amd64 \
		--tag fosrl/pangolin:$$MAJOR_TAG-amd64 \
		--tag fosrl/pangolin:$$MINOR_TAG-amd64 \
		--tag fosrl/pangolin:$(tag)-amd64 \
		--push . && \
	docker buildx build \
		--build-arg BUILD=oss \
		--build-arg DATABASE=pg \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg IMAGE_TITLE="Pangolin" \
		--build-arg IMAGE_DESCRIPTION="Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/amd64 \
		--tag fosrl/pangolin:postgresql-latest-amd64 \
		--tag fosrl/pangolin:postgresql-$$MAJOR_TAG-amd64 \
		--tag fosrl/pangolin:postgresql-$$MINOR_TAG-amd64 \
		--tag fosrl/pangolin:postgresql-$(tag)-amd64 \
		--push . && \
	docker buildx build \
		--build-arg BUILD=enterprise \
		--build-arg DATABASE=sqlite \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg LICENSE="Fossorial Commercial" \
		--build-arg IMAGE_TITLE="Pangolin EE" \
		--build-arg IMAGE_DESCRIPTION="Pangolin Enterprise Edition - Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/amd64 \
		--tag fosrl/pangolin:ee-latest-amd64 \
		--tag fosrl/pangolin:ee-$$MAJOR_TAG-amd64 \
		--tag fosrl/pangolin:ee-$$MINOR_TAG-amd64 \
		--tag fosrl/pangolin:ee-$(tag)-amd64 \
		--push . && \
	docker buildx build \
		--build-arg BUILD=enterprise \
		--build-arg DATABASE=pg \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg LICENSE="Fossorial Commercial" \
		--build-arg IMAGE_TITLE="Pangolin EE" \
		--build-arg IMAGE_DESCRIPTION="Pangolin Enterprise Edition - Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/amd64 \
		--tag fosrl/pangolin:ee-postgresql-latest-amd64 \
		--tag fosrl/pangolin:ee-postgresql-$$MAJOR_TAG-amd64 \
		--tag fosrl/pangolin:ee-postgresql-$$MINOR_TAG-amd64 \
		--tag fosrl/pangolin:ee-postgresql-$(tag)-amd64 \
		--push .

create-manifests:
	@if [ -z "$(tag)" ]; then \
		echo "Error: tag is required. Usage: make create-manifests tag=<tag>"; \
		exit 1; \
	fi
	@MAJOR_TAG=$$(echo $(tag) | cut -d. -f1); \
	MINOR_TAG=$$(echo $(tag) | cut -d. -f1,2); \
	echo "Creating multi-arch manifests for sqlite (oss)..." && \
	docker buildx imagetools create \
		--tag fosrl/pangolin:latest \
		--tag fosrl/pangolin:$$MAJOR_TAG \
		--tag fosrl/pangolin:$$MINOR_TAG \
		--tag fosrl/pangolin:$(tag) \
		fosrl/pangolin:latest-arm64 \
		fosrl/pangolin:latest-amd64 && \
	echo "Creating multi-arch manifests for postgresql (oss)..." && \
	docker buildx imagetools create \
		--tag fosrl/pangolin:postgresql-latest \
		--tag fosrl/pangolin:postgresql-$$MAJOR_TAG \
		--tag fosrl/pangolin:postgresql-$$MINOR_TAG \
		--tag fosrl/pangolin:postgresql-$(tag) \
		fosrl/pangolin:postgresql-latest-arm64 \
		fosrl/pangolin:postgresql-latest-amd64 && \
	echo "Creating multi-arch manifests for sqlite (enterprise)..." && \
	docker buildx imagetools create \
		--tag fosrl/pangolin:ee-latest \
		--tag fosrl/pangolin:ee-$$MAJOR_TAG \
		--tag fosrl/pangolin:ee-$$MINOR_TAG \
		--tag fosrl/pangolin:ee-$(tag) \
		fosrl/pangolin:ee-latest-arm64 \
		fosrl/pangolin:ee-latest-amd64 && \
	echo "Creating multi-arch manifests for postgresql (enterprise)..." && \
	docker buildx imagetools create \
		--tag fosrl/pangolin:ee-postgresql-latest \
		--tag fosrl/pangolin:ee-postgresql-$$MAJOR_TAG \
		--tag fosrl/pangolin:ee-postgresql-$$MINOR_TAG \
		--tag fosrl/pangolin:ee-postgresql-$(tag) \
		fosrl/pangolin:ee-postgresql-latest-arm64 \
		fosrl/pangolin:ee-postgresql-latest-amd64 && \
	echo "All multi-arch manifests created successfully!"

build-rc:
	@if [ -z "$(tag)" ]; then \
		echo "Error: tag is required. Usage: make build-release tag=<tag>"; \
		exit 1; \
	fi
	@CREATED=$$(date -u +"%Y-%m-%dT%H:%M:%SZ"); \
	REVISION=$$(git rev-parse HEAD 2>/dev/null || echo "unknown"); \
	docker buildx build \
		--build-arg BUILD=oss \
		--build-arg DATABASE=sqlite \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg IMAGE_TITLE="Pangolin" \
		--build-arg IMAGE_DESCRIPTION="Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/arm64,linux/amd64 \
		--tag fosrl/pangolin:$(tag) \
		--push . && \
	docker buildx build \
		--build-arg BUILD=oss \
		--build-arg DATABASE=pg \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg IMAGE_TITLE="Pangolin" \
		--build-arg IMAGE_DESCRIPTION="Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/arm64,linux/amd64 \
		--tag fosrl/pangolin:postgresql-$(tag) \
		--push . && \
	docker buildx build \
		--build-arg BUILD=enterprise \
		--build-arg DATABASE=sqlite \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg LICENSE="Fossorial Commercial" \
		--build-arg IMAGE_TITLE="Pangolin EE" \
		--build-arg IMAGE_DESCRIPTION="Pangolin Enterprise Edition - Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/arm64,linux/amd64 \
		--tag fosrl/pangolin:ee-$(tag) \
		--push . && \
	docker buildx build \
		--build-arg BUILD=enterprise \
		--build-arg DATABASE=pg \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg LICENSE="Fossorial Commercial" \
		--build-arg IMAGE_TITLE="Pangolin EE" \
		--build-arg IMAGE_DESCRIPTION="Pangolin Enterprise Edition - Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/arm64,linux/amd64 \
		--tag fosrl/pangolin:ee-postgresql-$(tag) \
		--push .

build-rc-arm:
	@if [ -z "$(tag)" ]; then \
		echo "Error: tag is required. Usage: make build-rc-arm tag=<tag>"; \
		exit 1; \
	fi
	@CREATED=$$(date -u +"%Y-%m-%dT%H:%M:%SZ"); \
	REVISION=$$(git rev-parse HEAD 2>/dev/null || echo "unknown"); \
	docker buildx build \
		--build-arg BUILD=oss \
		--build-arg DATABASE=sqlite \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg IMAGE_TITLE="Pangolin" \
		--build-arg IMAGE_DESCRIPTION="Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/arm64 \
		--tag fosrl/pangolin:$(tag)-arm64 \
		--push . && \
	docker buildx build \
		--build-arg BUILD=oss \
		--build-arg DATABASE=pg \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg IMAGE_TITLE="Pangolin" \
		--build-arg IMAGE_DESCRIPTION="Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/arm64 \
		--tag fosrl/pangolin:postgresql-$(tag)-arm64 \
		--push . && \
	docker buildx build \
		--build-arg BUILD=enterprise \
		--build-arg DATABASE=sqlite \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg LICENSE="Fossorial Commercial" \
		--build-arg IMAGE_TITLE="Pangolin EE" \
		--build-arg IMAGE_DESCRIPTION="Pangolin Enterprise Edition - Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/arm64 \
		--tag fosrl/pangolin:ee-$(tag)-arm64 \
		--push . && \
	docker buildx build \
		--build-arg BUILD=enterprise \
		--build-arg DATABASE=pg \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg LICENSE="Fossorial Commercial" \
		--build-arg IMAGE_TITLE="Pangolin EE" \
		--build-arg IMAGE_DESCRIPTION="Pangolin Enterprise Edition - Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/arm64 \
		--tag fosrl/pangolin:ee-postgresql-$(tag)-arm64 \
		--push .

build-rc-amd:
	@if [ -z "$(tag)" ]; then \
		echo "Error: tag is required. Usage: make build-rc-amd tag=<tag>"; \
		exit 1; \
	fi
	@CREATED=$$(date -u +"%Y-%m-%dT%H:%M:%SZ"); \
	REVISION=$$(git rev-parse HEAD 2>/dev/null || echo "unknown"); \
	docker buildx build \
		--build-arg BUILD=oss \
		--build-arg DATABASE=sqlite \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg IMAGE_TITLE="Pangolin" \
		--build-arg IMAGE_DESCRIPTION="Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/amd64 \
		--tag fosrl/pangolin:$(tag)-amd64 \
		--push . && \
	docker buildx build \
		--build-arg BUILD=oss \
		--build-arg DATABASE=pg \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg IMAGE_TITLE="Pangolin" \
		--build-arg IMAGE_DESCRIPTION="Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/amd64 \
		--tag fosrl/pangolin:postgresql-$(tag)-amd64 \
		--push . && \
	docker buildx build \
		--build-arg BUILD=enterprise \
		--build-arg DATABASE=sqlite \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg LICENSE="Fossorial Commercial" \
		--build-arg IMAGE_TITLE="Pangolin EE" \
		--build-arg IMAGE_DESCRIPTION="Pangolin Enterprise Edition - Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/amd64 \
		--tag fosrl/pangolin:ee-$(tag)-amd64 \
		--push . && \
	docker buildx build \
		--build-arg BUILD=enterprise \
		--build-arg DATABASE=pg \
		--build-arg VERSION=$(tag) \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg LICENSE="Fossorial Commercial" \
		--build-arg IMAGE_TITLE="Pangolin EE" \
		--build-arg IMAGE_DESCRIPTION="Pangolin Enterprise Edition - Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/amd64 \
		--tag fosrl/pangolin:ee-postgresql-$(tag)-amd64 \
		--push .

create-manifests-rc:
	@if [ -z "$(tag)" ]; then \
		echo "Error: tag is required. Usage: make create-manifests-rc tag=<tag>"; \
		exit 1; \
	fi
	@echo "Creating multi-arch manifests for RC sqlite (oss)..." && \
	docker buildx imagetools create \
		--tag fosrl/pangolin:$(tag) \
		fosrl/pangolin:$(tag)-arm64 \
		fosrl/pangolin:$(tag)-amd64 && \
	echo "Creating multi-arch manifests for RC postgresql (oss)..." && \
	docker buildx imagetools create \
		--tag fosrl/pangolin:postgresql-$(tag) \
		fosrl/pangolin:postgresql-$(tag)-arm64 \
		fosrl/pangolin:postgresql-$(tag)-amd64 && \
	echo "Creating multi-arch manifests for RC sqlite (enterprise)..." && \
	docker buildx imagetools create \
		--tag fosrl/pangolin:ee-$(tag) \
		fosrl/pangolin:ee-$(tag)-arm64 \
		fosrl/pangolin:ee-$(tag)-amd64 && \
	echo "Creating multi-arch manifests for RC postgresql (enterprise)..." && \
	docker buildx imagetools create \
		--tag fosrl/pangolin:ee-postgresql-$(tag) \
		fosrl/pangolin:ee-postgresql-$(tag)-arm64 \
		fosrl/pangolin:ee-postgresql-$(tag)-amd64 && \
	echo "All RC multi-arch manifests created successfully!"

build-arm:
	@CREATED=$$(date -u +"%Y-%m-%dT%H:%M:%SZ"); \
	REVISION=$$(git rev-parse HEAD 2>/dev/null || echo "unknown"); \
	docker buildx build \
		--build-arg VERSION=dev \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg IMAGE_TITLE="Pangolin" \
		--build-arg IMAGE_DESCRIPTION="Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/arm64 \
		-t fosrl/pangolin:latest .

build-x86:
	@CREATED=$$(date -u +"%Y-%m-%dT%H:%M:%SZ"); \
	REVISION=$$(git rev-parse HEAD 2>/dev/null || echo "unknown"); \
	docker buildx build \
		--build-arg VERSION=dev \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg IMAGE_TITLE="Pangolin" \
		--build-arg IMAGE_DESCRIPTION="Identity-aware VPN and proxy for remote access to anything, anywhere" \
		--platform linux/amd64 \
		-t fosrl/pangolin:latest .

dev-build-sqlite:
	@CREATED=$$(date -u +"%Y-%m-%dT%H:%M:%SZ"); \
	REVISION=$$(git rev-parse HEAD 2>/dev/null || echo "unknown"); \
	docker build \
		--build-arg DATABASE=sqlite \
		--build-arg VERSION=dev \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg IMAGE_TITLE="Pangolin" \
		--build-arg IMAGE_DESCRIPTION="Identity-aware VPN and proxy for remote access to anything, anywhere" \
		-t fosrl/pangolin:latest .

dev-build-pg:
	@CREATED=$$(date -u +"%Y-%m-%dT%H:%M:%SZ"); \
	REVISION=$$(git rev-parse HEAD 2>/dev/null || echo "unknown"); \
	docker build \
		--build-arg DATABASE=pg \
		--build-arg VERSION=dev \
		--build-arg REVISION=$$REVISION \
		--build-arg CREATED=$$CREATED \
		--build-arg IMAGE_TITLE="Pangolin" \
		--build-arg IMAGE_DESCRIPTION="Identity-aware VPN and proxy for remote access to anything, anywhere" \
		-t fosrl/pangolin:postgresql-latest .

test:
	docker run -it -p 3000:3000 -p 3001:3001 -p 3002:3002 -v ./config:/app/config fosrl/pangolin:latest

clean:
	docker rmi pangolin


================================================
FILE: README.md
================================================
<div align="center">
    <h2>
    <a href="https://pangolin.net/">
        <picture>
            <source media="(prefers-color-scheme: dark)" srcset="public/logo/word_mark_white.png">
            <img alt="Pangolin Logo" src="public/logo/word_mark_black.png" width="350">
        </picture>
    </a>
    </h2>
</div>

<div align="center">
  <h5>
      <a href="https://pangolin.net/">
        Website
      </a>
      <span> | </span>
      <a href="https://docs.pangolin.net/">
        Documentation
      </a>
      <span> | </span>
      <a href="mailto:contact@pangolin.net">
        Contact Us
      </a>
  </h5>
</div>

<div align="center">

[![Discord](https://img.shields.io/discord/1325658630518865980?logo=discord&style=flat-square)](https://discord.gg/HCJR8Xhme4)
[![Slack](https://img.shields.io/badge/chat-slack-yellow?style=flat-square&logo=slack)](https://pangolin.net/slack)
[![Docker](https://img.shields.io/docker/pulls/fosrl/pangolin?style=flat-square)](https://hub.docker.com/r/fosrl/pangolin)
![Stars](https://img.shields.io/github/stars/fosrl/pangolin?style=flat-square)
[![YouTube](https://img.shields.io/badge/YouTube-red?logo=youtube&logoColor=white&style=flat-square)](https://www.youtube.com/@pangolin-net)

</div>

<p align="center">
    <a href="https://docs.pangolin.net/careers/join-us">
        <img src="https://img.shields.io/badge/🚀_We're_Hiring!-Join_Our_Team-brightgreen?style=for-the-badge" alt="We're Hiring!" />
    </a>
</p>

<p align="center">
    <strong>
        Get started with Pangolin at <a href="https://app.pangolin.net/auth/signup">app.pangolin.net</a>
    </strong>
</p>

Pangolin is an open-source, identity-based remote access platform built on WireGuard that enables secure, seamless connectivity to private and public resources. Pangolin combines reverse proxy and VPN capabilities into one platform, providing browser-based access to web applications and client-based access to any private resources, all with zero-trust security and granular access control.

## Installation

- Check out the [quick install guide](https://docs.pangolin.net/self-host/quick-install) for how to install and set up Pangolin.
- Install from the [DigitalOcean marketplace](https://marketplace.digitalocean.com/apps/pangolin-ce-1?refcode=edf0480eeb81) for a one-click pre-configured installer.

<img src="public/screenshots/hero.png" />

## Deployment Options

| <img width=500 /> | Description |
|-----------------|--------------|
| **Pangolin Cloud** | Fully managed service with instant setup and pay-as-you-go pricing — no infrastructure required. Or, self-host your own [remote node](https://docs.pangolin.net/manage/remote-node/understanding-nodes) and connect to our control plane. |
| **Self-Host: Community Edition** | Free, open source, and licensed under AGPL-3. |
| **Self-Host: Enterprise Edition** | Licensed under Fossorial Commercial License. Free for personal and hobbyist use, and for businesses earning under \$100K USD annually. |

## Key Features

| <img width=500 />                                                                                                                                                                                                                                                                                                                                                                | <img width=500 />                                                  |
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------|
| **Connect remote networks with sites**<br /><br />Pangolin's lightweight site connectors create secure tunnels from remote networks without requiring public IP addresses or open ports. Sites make any network anywhere available for authorized access.                                                                                                                                                                                   | <img src="public/screenshots/sites.png" width=500 /><tr></tr>               |
| **Browser-based reverse proxy access**<br /><br />Expose web applications through identity and context-aware tunneled reverse proxies. Pangolin handles routing, load balancing, health checking, and automatic SSL certificates without exposing your network directly to the internet. Users access applications through any web browser with authentication and granular access control.                                                                                                  | <img src="public/clip.gif" width=500 /><tr></tr>          |
| **Client-based private resource access**<br /><br />Access private resources like SSH servers, databases, RDP, and entire network ranges through Pangolin clients. Intelligent NAT traversal enables connections even through restrictive firewalls, while DNS aliases provide friendly names and fast connections to resources across all your sites.                                                                                                                                                                                                | <img src="public/screenshots/private-resources.png" width=500 /><tr></tr>               |
| **Zero-trust granular access**<br /><br />Grant users access to specific resources, not entire networks. Unlike traditional VPNs that expose full network access, Pangolin's zero-trust model ensures users can only reach the applications and services you explicitly define, reducing security risk and attack surface.                                                                                                                                                                                    | <img src="public/screenshots/user-devices.png" width=500 /><tr></tr> |

## Download Clients

Download the Pangolin client for your platform:

- [Mac](https://pangolin.net/downloads/mac)
- [Windows](https://pangolin.net/downloads/windows)
- [Linux](https://pangolin.net/downloads/linux)
- [iOS](https://pangolin.net/downloads/ios)
- [Android](https://pangolin.net/downloads/android)

## Get Started

### Sign up now

Create an account at [app.pangolin.net](https://app.pangolin.net) to get started with Pangolin Cloud. A generous free tier is available.

### Check out the docs

We encourage everyone to read the full documentation first, which is
available at [docs.pangolin.net](https://docs.pangolin.net). This README provides only a very brief subset of
the docs to illustrate some basic ideas.

## Licensing

Pangolin is dual licensed under the AGPL-3 and the [Fossorial Commercial License](https://pangolin.net/fcl.html). For inquiries about commercial licensing, please contact us at [contact@pangolin.net](mailto:contact@pangolin.net).

## Contributions

Please see [CONTRIBUTING](./CONTRIBUTING.md) in the repository for guidelines and best practices.

---

WireGuard® is a registered trademark of Jason A. Donenfeld.


================================================
FILE: SECURITY.md
================================================
# Security Policy

If you discover a security vulnerability, please follow the steps below to responsibly disclose it to us:

1. **Do not create a public GitHub issue or discussion post.** This could put the security of other users at risk.
2. Send a detailed report to [security@pangolin.net](mailto:security@pangolin.net) or send a **private** message to a maintainer on [Discord](https://discord.gg/HCJR8Xhme4). Include:

-   Description and location of the vulnerability.
-   Potential impact of the vulnerability.
-   Steps to reproduce the vulnerability.
-   Potential solutions to fix the vulnerability.
-   Your name/handle and a link for recognition (optional).

We aim to address the issue as soon as possible.


================================================
FILE: bruno/API Keys/Create API Key.bru
================================================
meta {
  name: Create API Key
  type: http
  seq: 1
}

put {
  url: http://localhost:3000/api/v1/api-key
  body: json
  auth: inherit
}

body:json {
  {
    "isRoot": true
  }
}


================================================
FILE: bruno/API Keys/Delete API Key.bru
================================================
meta {
  name: Delete API Key
  type: http
  seq: 2
}

delete {
  url: http://localhost:3000/api/v1/api-key/dm47aacqxxn3ubj
  body: none
  auth: inherit
}


================================================
FILE: bruno/API Keys/List API Key Actions.bru
================================================
meta {
  name: List API Key Actions
  type: http
  seq: 6
}

get {
  url: http://localhost:3000/api/v1/api-key/ex0izu2c37fjz9x/actions
  body: none
  auth: inherit
}


================================================
FILE: bruno/API Keys/List Org API Keys.bru
================================================
meta {
  name: List Org API Keys
  type: http
  seq: 4
}

get {
  url: http://localhost:3000/api/v1/org/home-lab/api-keys
  body: none
  auth: inherit
}


================================================
FILE: bruno/API Keys/List Root API Keys.bru
================================================
meta {
  name: List Root API Keys
  type: http
  seq: 3
}

get {
  url: http://localhost:3000/api/v1/root/api-keys
  body: none
  auth: inherit
}


================================================
FILE: bruno/API Keys/Set API Key Actions.bru
================================================
meta {
  name: Set API Key Actions
  type: http
  seq: 5
}

post {
  url: http://localhost:3000/api/v1/api-key/ex0izu2c37fjz9x/actions
  body: json
  auth: inherit
}

body:json {
  {
    "actionIds": ["listSites"]
  }
}


================================================
FILE: bruno/API Keys/Set API Key Orgs.bru
================================================
meta {
  name: Set API Key Orgs
  type: http
  seq: 7
}

post {
  url: http://localhost:3000/api/v1/api-key/ex0izu2c37fjz9x/orgs
  body: json
  auth: inherit
}

body:json {
  {
    "orgIds": ["home-lab"]
  }
}


================================================
FILE: bruno/API Keys/folder.bru
================================================
meta {
  name: API Keys
}


================================================
FILE: bruno/Auth/2fa-disable.bru
================================================
meta {
  name: 2fa-disable
  type: http
  seq: 6
}

post {
  url: http://localhost:3000/api/v1/auth/2fa/disable
  body: json
  auth: none
}

body:json {
  {
      "password": "aaaaa-1A",
      "code": "377289"
  }
}


================================================
FILE: bruno/Auth/2fa-enable.bru
================================================
meta {
  name: 2fa-enable
  type: http
  seq: 4
}

post {
  url: http://localhost:3000/api/v1/auth/2fa/enable
  body: json
  auth: none
}

body:json {
  {
      "code": "374138"
  }
}


================================================
FILE: bruno/Auth/2fa-request.bru
================================================
meta {
  name: 2fa-request
  type: http
  seq: 5
}

post {
  url: http://localhost:3000/api/v1/auth/2fa/request
  body: json
  auth: none
}

body:json {
  {
      "password": "aaaaa-1A"
  }
}


================================================
FILE: bruno/Auth/change-password.bru
================================================
meta {
  name: change-password
  type: http
  seq: 9
}

post {
  url: http://localhost:3000/api/v1/auth/change-password
  body: json
  auth: none
}

body:json {
  {
      "oldPassword": "",
      "newPassword": ""
  }
}


================================================
FILE: bruno/Auth/login.bru
================================================
meta {
  name: login
  type: http
  seq: 1
}

post {
  url: http://localhost:3000/api/v1/auth/login
  body: json
  auth: none
}

body:json {
  {
    "email": "admin@fosrl.io",
    "password": "Password123!"
  }
}


================================================
FILE: bruno/Auth/logout.bru
================================================
meta {
  name: logout
  type: http
  seq: 3
}

post {
  url: http://localhost:4000/api/v1/auth/logout
  body: none
  auth: none
}


================================================
FILE: bruno/Auth/reset-password-request.bru
================================================
meta {
  name: reset-password-request
  type: http
  seq: 10
}

post {
  url: http://localhost:3000/api/v1/auth/reset-password/request
  body: json
  auth: none
}

body:json {
  {
      "email": "milo@pangolin.net"
  }
}


================================================
FILE: bruno/Auth/reset-password.bru
================================================
meta {
  name: reset-password
  type: http
  seq: 11
}

post {
  url: http://localhost:3000/api/v1/auth/reset-password
  body: json
  auth: none
}

body:json {
  {
      "token": "3uhsbom72dwdhboctwrtntyd6jrlg4jtf5oaxy4k",
      "newPassword": "aaaaa-1A",
      "code": "6irqCGR3"
  }
}


================================================
FILE: bruno/Auth/signup.bru
================================================
meta {
  name: signup
  type: http
  seq: 2
}

put {
  url: http://localhost:3000/api/v1/auth/signup
  body: json
  auth: none
}

body:json {
  {
    "email": "numbat@pangolin.net",
    "password": "Password123!"
  }
}


================================================
FILE: bruno/Auth/verify-email-request.bru
================================================
meta {
  name: verify-email-request
  type: http
  seq: 8
}

post {
  url: http://localhost:3000/api/v1/auth/verify-email/request
  body: none
  auth: none
}


================================================
FILE: bruno/Auth/verify-email.bru
================================================
meta {
  name: verify-email
  type: http
  seq: 7
}

post {
  url: http://localhost:3000/api/v1/auth/verify-email
  body: json
  auth: none
}

body:json {
  {
      "code": "50317187"
  }
}


================================================
FILE: bruno/Auth/verify-user.bru
================================================
meta {
  name: verify-user
  type: http
  seq: 4
}

get {
  url: http://localhost:3001/api/v1/badger/verify-user?sessionId=mb52273jkb6t3oys2bx6ur5x7rcrkl26c7warg3e
  body: none
  auth: none
}

params:query {
  sessionId: mb52273jkb6t3oys2bx6ur5x7rcrkl26c7warg3e
}


================================================
FILE: bruno/Clients/createClient.bru
================================================
meta {
  name: createClient
  type: http
  seq: 1
}

put {
  url: http://localhost:3000/api/v1/site/1/client
  body: json
  auth: none
}

body:json {
  {
      "siteId": 1,
      "name": "test",
      "type": "olm",
      "subnet": "100.90.129.4/30",
      "olmId": "029yzunhx6nh3y5",
      "secret": "l0ymp075y3d4rccb25l6sqpgar52k09etunui970qq5gj7x6"
  }
}


================================================
FILE: bruno/Clients/pickClientDefaults.bru
================================================
meta {
  name: pickClientDefaults
  type: http
  seq: 2
}

get {
  url: http://localhost:3000/api/v1/site/1/pick-client-defaults
  body: none
  auth: none
}


================================================
FILE: bruno/IDP/Create OIDC Provider.bru
================================================
meta {
  name: Create OIDC Provider
  type: http
  seq: 1
}

put {
  url: http://localhost:3000/api/v1/org/home-lab/idp/oidc
  body: json
  auth: inherit
}

body:json {
  {
    "clientId": "JJoSvHCZcxnXT2sn6CObj6a21MuKNRXs3kN5wbys",
    "clientSecret": "2SlGL2wOGgMEWLI9yUuMAeFxre7qSNJVnXMzyepdNzH1qlxYnC4lKhhQ6a157YQEkYH3vm40KK4RCqbYiF8QIweuPGagPX3oGxEj2exwutoXFfOhtq4hHybQKoFq01Z3",
    "authUrl": "http://localhost:9000/application/o/authorize/",
    "tokenUrl": "http://localhost:9000/application/o/token/",
    "scopes": ["email", "openid", "profile"],
    "userIdentifier": "email"
  }
}


================================================
FILE: bruno/IDP/Generate OIDC URL.bru
================================================
meta {
  name: Generate OIDC URL
  type: http
  seq: 2
}

get {
  url: http://localhost:3000/api/v1
  body: none
  auth: inherit
}


================================================
FILE: bruno/IDP/folder.bru
================================================
meta {
  name: IDP
}


================================================
FILE: bruno/Internal/Traefik Config.bru
================================================
meta {
  name: Traefik Config
  type: http
  seq: 1
}

get {
  url: http://localhost:3001/api/v1/traefik-config
  body: none
  auth: inherit
}


================================================
FILE: bruno/Internal/folder.bru
================================================
meta {
  name: Internal
}


================================================
FILE: bruno/Newt/Create Newt.bru
================================================
meta {
  name: Create Newt
  type: http
  seq: 2
}

get {
  url: http://localhost:3000/api/v1/newt
  body: none
  auth: none
}


================================================
FILE: bruno/Newt/Get Token.bru
================================================
meta {
  name: Get Token
  type: http
  seq: 1
}

get {
  url: http://localhost:3000/api/v1/auth/newt/get-token
  body: json
  auth: none
}

body:json {
  {
   "newtId": "o0d4rdxq3stnz7b",
    "secret": "sy7l09fnaesd03iwrfp9m3qf0ryn19g0zf3dqieaazb4k7vk"
  }
}


================================================
FILE: bruno/Olm/createOlm.bru
================================================
meta {
  name: createOlm
  type: http
  seq: 1
}

put {
  url: http://localhost:3000/api/v1/olm
  body: none
  auth: inherit
}

settings {
  encodeUrl: true
}


================================================
FILE: bruno/Olm/folder.bru
================================================
meta {
  name: Olm
  seq: 15
}

auth {
  mode: inherit
}


================================================
FILE: bruno/Orgs/Check Id.bru
================================================
meta {
  name: Check Id
  type: http
  seq: 2
}

get {
  url: http://localhost:3000/api/v1/org/checkId
  body: none
  auth: none
}


================================================
FILE: bruno/Orgs/listOrgs.bru
================================================
meta {
  name: listOrgs
  type: http
  seq: 1
}

get {
  url: 
  body: none
  auth: none
}


================================================
FILE: bruno/Remote Exit Node/createRemoteExitNode.bru
================================================
meta {
  name: createRemoteExitNode
  type: http
  seq: 1
}

put {
  url: http://localhost:4000/api/v1/org/org_i21aifypnlyxur2/remote-exit-node
  body: none
  auth: none
}


================================================
FILE: bruno/Resources/listResourcesByOrg.bru
================================================
meta {
  name: listResourcesByOrg
  type: http
  seq: 1
}

get {
  url: 
  body: none
  auth: none
}


================================================
FILE: bruno/Resources/listResourcesBySite.bru
================================================
meta {
  name: listResourcesBySite
  type: http
  seq: 2
}

get {
  url: http://localhost:3000/api/v1/site/1/resources?limit=10&offset=0
  body: none
  auth: none
}

params:query {
  limit: 10
  offset: 0
}


================================================
FILE: bruno/Sites/Get Site.bru
================================================
meta {
  name: Get Site
  type: http
  seq: 2
}

get {
  url: http://localhost:3000/api/v1/org/test/sites/mexican-mole-lizard-windy
  body: none
  auth: none
}


================================================
FILE: bruno/Sites/listSites.bru
================================================
meta {
  name: listSites
  type: http
  seq: 1
}

get {
  url: 
  body: none
  auth: none
}


================================================
FILE: bruno/Targets/listTargets.bru
================================================
meta {
  name: listTargets
  type: http
  seq: 1
}

get {
  url: http://localhost:3000/api/v1/resource/web.main.localhost/targets?limit=10&offset=0
  body: none
  auth: none
}

params:query {
  limit: 10
  offset: 0
}


================================================
FILE: bruno/Test.bru
================================================
meta {
  name: Test
  type: http
  seq: 2
}

get {
  url: http://localhost:3000/api/v1
  body: none
  auth: inherit
}


================================================
FILE: bruno/Traefik/traefik-config.bru
================================================
meta {
  name: traefik-config
  type: http
  seq: 1
}

get {
  url: http://localhost:3001/api/v1/traefik-config
  body: none
  auth: none
}


================================================
FILE: bruno/Users/adminListUsers.bru
================================================
meta {
  name: adminListUsers
  type: http
  seq: 2
}

get {
  url: http://localhost:3000/api/v1/users
  body: none
  auth: none
}


================================================
FILE: bruno/Users/adminRemoveUser.bru
================================================
meta {
  name: adminRemoveUser
  type: http
  seq: 3
}

delete {
  url: http://localhost:3000/api/v1/user/ky5r7ivqs8wc7u4
  body: none
  auth: none
}


================================================
FILE: bruno/Users/getUser.bru
================================================
meta {
  name: getUser
  type: http
  seq: 1
}

get {
  url: 
  body: none
  auth: none
}


================================================
FILE: bruno/bruno.json
================================================
{
  "version": "1",
  "name": "Pangolin",
  "type": "collection",
  "ignore": [
    "node_modules",
    ".git"
  ],
  "presets": {
    "requestType": "http",
    "requestUrl": "http://localhost:3000/api/v1"
  }
}

================================================
FILE: cli/commands/clearExitNodes.ts
================================================
import { CommandModule } from "yargs";
import { db, exitNodes } from "@server/db";
import { eq } from "drizzle-orm";

type ClearExitNodesArgs = { };

export const clearExitNodes: CommandModule<
    {},
    ClearExitNodesArgs
> = {
    command: "clear-exit-nodes",
    describe:
        "Clear all exit nodes from the database",
    // no args
    builder: (yargs) => {
        return yargs;
    },
    handler: async (argv: {}) => {
        try {

            console.log(`Clearing all exit nodes from the database`);

            // Delete all exit nodes
            const deletedCount = await db
                .delete(exitNodes)
                .where(eq(exitNodes.exitNodeId, exitNodes.exitNodeId))  .returning();; // delete all

            console.log(`Deleted ${deletedCount.length} exit node(s) from the database`);

            process.exit(0);
        } catch (error) {
            console.error("Error:", error);
            process.exit(1);
        }
    }
};


================================================
FILE: cli/commands/clearLicenseKeys.ts
================================================
import { CommandModule } from "yargs";
import { db, licenseKey } from "@server/db";
import { eq } from "drizzle-orm";

type ClearLicenseKeysArgs = { };

export const clearLicenseKeys: CommandModule<
    {},
    ClearLicenseKeysArgs
> = {
    command: "clear-license-keys",
    describe:
        "Clear all license keys from the database",
    // no args
    builder: (yargs) => {
        return yargs;
    },
    handler: async (argv: {}) => {
        try {

            console.log(`Clearing all license keys from the database`);

            // Delete all license keys
            const deletedCount = await db
                .delete(licenseKey)
                .where(eq(licenseKey.licenseKeyId, licenseKey.licenseKeyId))  .returning();; // delete all

            console.log(`Deleted ${deletedCount.length} license key(s) from the database`);

            process.exit(0);
        } catch (error) {
            console.error("Error:", error);
            process.exit(1);
        }
    }
};


================================================
FILE: cli/commands/deleteClient.ts
================================================
import { CommandModule } from "yargs";
import { db, clients, olms, currentFingerprint, userClients, approvals } from "@server/db";
import { eq, and, inArray } from "drizzle-orm";

type DeleteClientArgs = {
    orgId: string;
    niceId: string;
};

export const deleteClient: CommandModule<{}, DeleteClientArgs> = {
    command: "delete-client",
    describe:
        "Delete a client and all associated data (OLMs, current fingerprint, userClients, approvals). Snapshots are preserved.",
    builder: (yargs) => {
        return yargs
            .option("orgId", {
                type: "string",
                demandOption: true,
                describe: "The organization ID"
            })
            .option("niceId", {
                type: "string",
                demandOption: true,
                describe: "The client niceId (identifier)"
            });
    },
    handler: async (argv: { orgId: string; niceId: string }) => {
        try {
            const { orgId, niceId } = argv;

            console.log(
                `Deleting client with orgId: ${orgId}, niceId: ${niceId}...`
            );

            // Find the client
            const [client] = await db
                .select()
                .from(clients)
                .where(and(eq(clients.orgId, orgId), eq(clients.niceId, niceId)))
                .limit(1);

            if (!client) {
                console.error(
                    `Error: Client with orgId "${orgId}" and niceId "${niceId}" not found.`
                );
                process.exit(1);
            }

            const clientId = client.clientId;
            console.log(`Found client with clientId: ${clientId}`);

            // Find all OLMs associated with this client
            const associatedOlms = await db
                .select()
                .from(olms)
                .where(eq(olms.clientId, clientId));

            console.log(`Found ${associatedOlms.length} OLM(s) associated with this client`);

            // Delete in a transaction to ensure atomicity
            await db.transaction(async (trx) => {
                // Delete currentFingerprint entries for the associated OLMs
                // Note: We delete these explicitly before deleting OLMs to ensure
                // we have control, even though cascade would handle it
                let fingerprintCount = 0;
                if (associatedOlms.length > 0) {
                    const olmIds = associatedOlms.map((olm) => olm.olmId);
                    const deletedFingerprints = await trx
                        .delete(currentFingerprint)
                        .where(inArray(currentFingerprint.olmId, olmIds))
                        .returning();
                    fingerprintCount = deletedFingerprints.length;
                }
                console.log(`Deleted ${fingerprintCount} current fingerprint(s)`);

                // Delete OLMs
                // Note: OLMs have onDelete: "set null" for clientId, so we need to delete them explicitly
                const deletedOlms = await trx
                    .delete(olms)
                    .where(eq(olms.clientId, clientId))
                    .returning();
                console.log(`Deleted ${deletedOlms.length} OLM(s)`);

                // Delete approvals
                // Note: Approvals have onDelete: "cascade" but we delete explicitly for clarity
                const deletedApprovals = await trx
                    .delete(approvals)
                    .where(eq(approvals.clientId, clientId))
                    .returning();
                console.log(`Deleted ${deletedApprovals.length} approval(s)`);

                // Delete userClients
                // Note: userClients have onDelete: "cascade" but we delete explicitly for clarity
                const deletedUserClients = await trx
                    .delete(userClients)
                    .where(eq(userClients.clientId, clientId))
                    .returning();
                console.log(`Deleted ${deletedUserClients.length} userClient association(s)`);

                // Finally, delete the client itself
                const deletedClients = await trx
                    .delete(clients)
                    .where(eq(clients.clientId, clientId))
                    .returning();
                console.log(`Deleted client: ${deletedClients[0]?.name || niceId}`);
            });

            console.log("\nClient deletion completed successfully!");
            console.log("\nSummary:");
            console.log(`  - Client: ${niceId} (clientId: ${clientId})`);
            console.log(`  - Olm(s): ${associatedOlms.length}`);
            console.log(`  - Current fingerprints: deleted`);
            console.log(`  - Approvals: deleted`);
            console.log(`  - UserClients: deleted`);
            console.log(`  - Snapshots: preserved (not deleted)`);

            process.exit(0);
        } catch (error) {
            console.error("Error deleting client:", error);
            process.exit(1);
        }
    }
};


================================================
FILE: cli/commands/generateOrgCaKeys.ts
================================================
import { CommandModule } from "yargs";
import { db, orgs } from "@server/db";
import { eq } from "drizzle-orm";
import { encrypt } from "@server/lib/crypto";
import { configFilePath1, configFilePath2 } from "@server/lib/consts";
import { generateCA } from "@server/lib/sshCA";
import fs from "fs";
import yaml from "js-yaml";

type GenerateOrgCaKeysArgs = {
    orgId: string;
    secret?: string;
    force?: boolean;
};

export const generateOrgCaKeys: CommandModule<{}, GenerateOrgCaKeysArgs> = {
    command: "generate-org-ca-keys",
    describe:
        "Generate SSH CA public/private key pair for an organization and store them in the database (private key encrypted with server secret)",
    builder: (yargs) => {
        return yargs
            .option("orgId", {
                type: "string",
                demandOption: true,
                describe: "The organization ID"
            })
            .option("secret", {
                type: "string",
                describe:
                    "Server secret used to encrypt the CA private key. If omitted, read from config file (config.yml or config.yaml)."
            })
            .option("force", {
                type: "boolean",
                default: false,
                describe:
                    "Overwrite existing CA keys for the org if they already exist"
            });
    },
    handler: async (argv: {
        orgId: string;
        secret?: string;
        force?: boolean;
    }) => {
        try {
            const { orgId, force } = argv;
            let secret = argv.secret;

            if (!secret) {
                const configPath = fs.existsSync(configFilePath1)
                    ? configFilePath1
                    : fs.existsSync(configFilePath2)
                      ? configFilePath2
                      : null;

                if (!configPath) {
                    console.error(
                        "Error: No server secret provided and config file not found. " +
                            "Expected config.yml or config.yaml in the config directory, or pass --secret."
                    );
                    process.exit(1);
                }

                const configContent = fs.readFileSync(configPath, "utf8");
                const config = yaml.load(configContent) as {
                    server?: { secret?: string };
                };

                if (!config?.server?.secret) {
                    console.error(
                        "Error: No server.secret in config file. Pass --secret or set server.secret in config."
                    );
                    process.exit(1);
                }
                secret = config.server.secret;
            }

            const [org] = await db
                .select({
                    orgId: orgs.orgId,
                    sshCaPrivateKey: orgs.sshCaPrivateKey,
                    sshCaPublicKey: orgs.sshCaPublicKey
                })
                .from(orgs)
                .where(eq(orgs.orgId, orgId))
                .limit(1);

            if (!org) {
                console.error(`Error: Organization with orgId "${orgId}" not found.`);
                process.exit(1);
            }

            if (org.sshCaPrivateKey != null || org.sshCaPublicKey != null) {
                if (!force) {
                    console.error(
                        "Error: This organization already has CA keys. Use --force to overwrite."
                    );
                    process.exit(1);
                }
            }

            const ca = generateCA(`pangolin-ssh-ca-${orgId}`);
            const encryptedPrivateKey = encrypt(ca.privateKeyPem, secret);

            await db
                .update(orgs)
                .set({
                    sshCaPrivateKey: encryptedPrivateKey,
                    sshCaPublicKey: ca.publicKeyOpenSSH
                })
                .where(eq(orgs.orgId, orgId));

            console.log("SSH CA keys generated and stored for org:", orgId);
            console.log("\nPublic key (OpenSSH format):");
            console.log(ca.publicKeyOpenSSH);
            process.exit(0);
        } catch (error) {
            console.error("Error generating org CA keys:", error);
            process.exit(1);
        }
    }
};


================================================
FILE: cli/commands/resetUserSecurityKeys.ts
================================================
import { CommandModule } from "yargs";
import { db, users, securityKeys } from "@server/db";
import { eq } from "drizzle-orm";

type ResetUserSecurityKeysArgs = {
    email: string;
};

export const resetUserSecurityKeys: CommandModule<
    {},
    ResetUserSecurityKeysArgs
> = {
    command: "reset-user-security-keys",
    describe:
        "Reset a user's security keys (passkeys) by deleting all their webauthn credentials",
    builder: (yargs) => {
        return yargs.option("email", {
            type: "string",
            demandOption: true,
            describe: "User email address"
        });
    },
    handler: async (argv: { email: string }) => {
        try {
            const { email } = argv;

            console.log(`Looking for user with email: ${email}`);

            // Find the user by email
            const [user] = await db
                .select()
                .from(users)
                .where(eq(users.email, email))
                .limit(1);

            if (!user) {
                console.error(`User with email '${email}' not found`);
                process.exit(1);
            }

            console.log(`Found user: ${user.email} (ID: ${user.userId})`);

            // Check if user has any security keys
            const userSecurityKeys = await db
                .select()
                .from(securityKeys)
                .where(eq(securityKeys.userId, user.userId));

            if (userSecurityKeys.length === 0) {
                console.log(`User '${email}' has no security keys to reset`);
                process.exit(0);
            }

            console.log(
                `Found ${userSecurityKeys.length} security key(s) for user '${email}'`
            );

            // Delete all security keys for the user
            await db
                .delete(securityKeys)
                .where(eq(securityKeys.userId, user.userId));

            console.log(`Successfully reset security keys for user '${email}'`);
            console.log(`Deleted ${userSecurityKeys.length} security key(s)`);

            process.exit(0);
        } catch (error) {
            console.error("Error:", error);
            process.exit(1);
        }
    }
};


================================================
FILE: cli/commands/rotateServerSecret.ts
================================================
import { CommandModule } from "yargs";
import { db, idpOidcConfig, licenseKey } from "@server/db";
import { encrypt, decrypt } from "@server/lib/crypto";
import { configFilePath1, configFilePath2 } from "@server/lib/consts";
import { eq } from "drizzle-orm";
import fs from "fs";
import yaml from "js-yaml";

type RotateServerSecretArgs = {
    "old-secret": string;
    "new-secret": string;
    force?: boolean;
};

export const rotateServerSecret: CommandModule<
    {},
    RotateServerSecretArgs
> = {
    command: "rotate-server-secret",
    describe:
        "Rotate the server secret by decrypting all encrypted values with the old secret and re-encrypting with a new secret",
    builder: (yargs) => {
        return yargs
            .option("old-secret", {
                type: "string",
                demandOption: true,
                describe: "The current server secret (for verification)"
            })
            .option("new-secret", {
                type: "string",
                demandOption: true,
                describe: "The new server secret to use"
            })
            .option("force", {
                type: "boolean",
                default: false,
                describe:
                    "Force rotation even if the old secret doesn't match the config file. " +
                    "Use this if you know the old secret is correct but the config file is out of sync. " +
                    "WARNING: This will attempt to decrypt all values with the provided old secret. " +
                    "If the old secret is incorrect, the rotation will fail or corrupt data."
            });
    },
    handler: async (argv: {
        "old-secret": string;
        "new-secret": string;
        force?: boolean;
    }) => {
        try {
            // Determine which config file exists
            const configPath = fs.existsSync(configFilePath1)
                ? configFilePath1
                : fs.existsSync(configFilePath2)
                  ? configFilePath2
                  : null;

            if (!configPath) {
                console.error(
                    "Error: Config file not found. Expected config.yml or config.yaml in the config directory."
                );
                process.exit(1);
            }

            // Read current config
            const configContent = fs.readFileSync(configPath, "utf8");
            const config = yaml.load(configContent) as any;

            if (!config?.server?.secret) {
                console.error(
                    "Error: No server secret found in config file. Cannot rotate."
                );
                process.exit(1);
            }

            const configSecret = config.server.secret;
            const oldSecret = argv["old-secret"];
            const newSecret = argv["new-secret"];
            const force = argv.force || false;

            // Verify that the provided old secret matches the one in config
            if (configSecret !== oldSecret) {
                if (!force) {
                    console.error(
                        "Error: The provided old secret does not match the secret in the config file."
                    );
                    console.error(
                        "\nIf you are certain the old secret is correct and the config file is out of sync,"
                    );
                    console.error(
                        "you can use the --force flag to bypass this check."
                    );
                    console.error(
                        "\nWARNING: Using --force with an incorrect old secret will cause the rotation to fail"
                    );
                    console.error(
                        "or corrupt encrypted data. Only use --force if you are absolutely certain."
                    );
                    process.exit(1);
                } else {
                    console.warn(
                        "\nWARNING: Using --force flag. Bypassing old secret verification."
                    );
                    console.warn(
                        "The provided old secret does not match the config file, but proceeding anyway."
                    );
                    console.warn(
                        "If the old secret is incorrect, this operation will fail or corrupt data.\n"
                    );
                }
            }

            // Validate new secret
            if (newSecret.length < 8) {
                console.error(
                    "Error: New secret must be at least 8 characters long"
                );
                process.exit(1);
            }

            if (oldSecret === newSecret) {
                console.error("Error: New secret must be different from old secret");
                process.exit(1);
            }

            console.log("Starting server secret rotation...");
            console.log("This will decrypt and re-encrypt all encrypted values in the database.");

            // Read all data first
            console.log("\nReading encrypted data from database...");
            const idpConfigs = await db.select().from(idpOidcConfig);
            const licenseKeys = await db.select().from(licenseKey);

            console.log(`Found ${idpConfigs.length} OIDC IdP configuration(s)`);
            console.log(`Found ${licenseKeys.length} license key(s)`);

            // Prepare all decrypted and re-encrypted values
            console.log("\nDecrypting and re-encrypting values...");

            type IdpUpdate = {
                idpOauthConfigId: number;
                encryptedClientId: string;
                encryptedClientSecret: string;
            };

            type LicenseKeyUpdate = {
                oldLicenseKeyId: string;
                newLicenseKeyId: string;
                encryptedToken: string;
                encryptedInstanceId: string;
            };

            const idpUpdates: IdpUpdate[] = [];
            const licenseKeyUpdates: LicenseKeyUpdate[] = [];

            // Process idpOidcConfig entries
            for (const idpConfig of idpConfigs) {
                try {
                    // Decrypt with old secret
                    const decryptedClientId = decrypt(idpConfig.clientId, oldSecret);
                    const decryptedClientSecret = decrypt(
                        idpConfig.clientSecret,
                        oldSecret
                    );

                    // Re-encrypt with new secret
                    const encryptedClientId = encrypt(decryptedClientId, newSecret);
                    const encryptedClientSecret = encrypt(
                        decryptedClientSecret,
                        newSecret
                    );

                    idpUpdates.push({
                        idpOauthConfigId: idpConfig.idpOauthConfigId,
                        encryptedClientId,
                        encryptedClientSecret
                    });
                } catch (error) {
                    console.error(
                        `Error processing IdP config ${idpConfig.idpOauthConfigId}:`,
                        error
                    );
                    throw error;
                }
            }

            // Process licenseKey entries
            for (const key of licenseKeys) {
                try {
                    // Decrypt with old secret
                    const decryptedLicenseKeyId = decrypt(key.licenseKeyId, oldSecret);
                    const decryptedToken = decrypt(key.token, oldSecret);
                    const decryptedInstanceId = decrypt(key.instanceId, oldSecret);

                    // Re-encrypt with new secret
                    const encryptedLicenseKeyId = encrypt(
                        decryptedLicenseKeyId,
                        newSecret
                    );
                    const encryptedToken = encrypt(decryptedToken, newSecret);
                    const encryptedInstanceId = encrypt(
                        decryptedInstanceId,
                        newSecret
                    );

                    licenseKeyUpdates.push({
                        oldLicenseKeyId: key.licenseKeyId,
                        newLicenseKeyId: encryptedLicenseKeyId,
                        encryptedToken,
                        encryptedInstanceId
                    });
                } catch (error) {
                    console.error(
                        `Error processing license key ${key.licenseKeyId}:`,
                        error
                    );
                    throw error;
                }
            }

            // Perform all database updates in a single transaction
            console.log("\nUpdating database in transaction...");
            await db.transaction(async (trx) => {
                // Update idpOidcConfig entries
                for (const update of idpUpdates) {
                    await trx
                        .update(idpOidcConfig)
                        .set({
                            clientId: update.encryptedClientId,
                            clientSecret: update.encryptedClientSecret
                        })
                        .where(
                            eq(
                                idpOidcConfig.idpOauthConfigId,
                                update.idpOauthConfigId
                            )
                        );
                }

                // Update licenseKey entries (delete old, insert new)
                for (const update of licenseKeyUpdates) {
                    // Delete old entry
                    await trx
                        .delete(licenseKey)
                        .where(eq(licenseKey.licenseKeyId, update.oldLicenseKeyId));

                    // Insert new entry with re-encrypted values
                    await trx.insert(licenseKey).values({
                        licenseKeyId: update.newLicenseKeyId,
                        token: update.encryptedToken,
                        instanceId: update.encryptedInstanceId
                    });
                }
            });

            console.log(`Rotated ${idpUpdates.length} OIDC IdP configuration(s)`);
            console.log(`Rotated ${licenseKeyUpdates.length} license key(s)`);

            // Update config file with new secret
            console.log("\nUpdating config file...");
            config.server.secret = newSecret;
            const newConfigContent = yaml.dump(config, {
                indent: 2,
                lineWidth: -1
            });
            fs.writeFileSync(configPath, newConfigContent, "utf8");

            console.log(`Updated config file: ${configPath}`);

            console.log("\nServer secret rotation completed successfully!");
            console.log(`\nSummary:`);
            console.log(`  - OIDC IdP configurations: ${idpUpdates.length}`);
            console.log(`  - License keys: ${licenseKeyUpdates.length}`);
            console.log(
                `\n  IMPORTANT: Restart the server for the new secret to take effect.`
            );

            process.exit(0);
        } catch (error) {
            console.error("Error rotating server secret:", error);
            process.exit(1);
        }
    }
};



================================================
FILE: cli/commands/setAdminCredentials.ts
================================================
import { CommandModule } from "yargs";
import { hashPassword, verifyPassword } from "@server/auth/password";
import { db, resourceSessions, sessions } from "@server/db";
import { users } from "@server/db";
import { eq, inArray } from "drizzle-orm";
import moment from "moment";
import { fromError } from "zod-validation-error";
import { passwordSchema } from "@server/auth/passwordSchema";
import { UserType } from "@server/types/UserTypes";
import { generateRandomString, RandomReader } from "@oslojs/crypto/random";

type SetAdminCredentialsArgs = {
    email: string;
    password: string;
};

export const setAdminCredentials: CommandModule<{}, SetAdminCredentialsArgs> = {
    command: "set-admin-credentials",
    describe: "Set the server admin credentials",
    builder: (yargs) => {
        return yargs
            .option("email", {
                type: "string",
                demandOption: true,
                describe: "Admin email address"
            })
            .option("password", {
                type: "string",
                demandOption: true,
                describe: "Admin password"
            });
    },
    handler: async (argv: { email: string; password: string }) => {
        try {
            const { password } = argv;
            let { email } = argv;
            email = email.trim().toLowerCase();

            const parsed = passwordSchema.safeParse(password);

            if (!parsed.success) {
                throw Error(
                    `Invalid server admin password: ${fromError(parsed.error).toString()}`
                );
            }

            const passwordHash = await hashPassword(password);

            await db.transaction(async (trx) => {
                try {
                    const [existing] = await trx
                        .select()
                        .from(users)
                        .where(eq(users.serverAdmin, true));

                    if (existing) {
                        const passwordChanged = !(await verifyPassword(
                            password,
                            existing.passwordHash!
                        ));

                        if (passwordChanged) {
                            await trx
                                .update(users)
                                .set({ passwordHash })
                                .where(eq(users.userId, existing.userId));

                            await invalidateAllSessions(existing.userId);
                            console.log("Server admin password updated");
                        }

                        if (existing.email !== email) {
                            await trx
                                .update(users)
                                .set({ email, username: email })
                                .where(eq(users.userId, existing.userId));

                            console.log("Server admin email updated");
                        }
                    } else {
                        const userId = generateId(15);

                        await trx.update(users).set({ serverAdmin: false });

                        await db.insert(users).values({
                            userId: userId,
                            email: email,
                            type: UserType.Internal,
                            username: email,
                            passwordHash,
                            dateCreated: moment().toISOString(),
                            serverAdmin: true,
                            emailVerified: true,
                            lastPasswordChange: new Date().getTime()
                        });

                        console.log("Server admin created");
                    }
                } catch (e) {
                    console.error("Failed to set admin credentials", e);
                    trx.rollback();
                    throw e;
                }
            });

            console.log("Admin credentials updated successfully");
            process.exit(0);
        } catch (error) {
            console.error("Error:", error);
            process.exit(1);
        }
    }
};

export async function invalidateAllSessions(userId: string): Promise<void> {
    try {
        await db.transaction(async (trx) => {
            const userSessions = await trx
                .select()
                .from(sessions)
                .where(eq(sessions.userId, userId));
            await trx.delete(resourceSessions).where(
                inArray(
                    resourceSessions.userSessionId,
                    userSessions.map((s) => s.sessionId)
                )
            );
            await trx.delete(sessions).where(eq(sessions.userId, userId));
        });
    } catch (e) {
        console.log("Failed to all invalidate user sessions", e);
    }
}

const random: RandomReader = {
    read(bytes: Uint8Array): void {
        crypto.getRandomValues(bytes);
    }
};

export function generateId(length: number): string {
    const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
    return generateRandomString(random, alphabet, length);
}


================================================
FILE: cli/index.ts
================================================
#!/usr/bin/env node

import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { setAdminCredentials } from "@cli/commands/setAdminCredentials";
import { resetUserSecurityKeys } from "@cli/commands/resetUserSecurityKeys";
import { clearExitNodes } from "./commands/clearExitNodes";
import { rotateServerSecret } from "./commands/rotateServerSecret";
import { clearLicenseKeys } from "./commands/clearLicenseKeys";
import { deleteClient } from "./commands/deleteClient";
import { generateOrgCaKeys } from "./commands/generateOrgCaKeys";

yargs(hideBin(process.argv))
    .scriptName("pangctl")
    .command(setAdminCredentials)
    .command(resetUserSecurityKeys)
    .command(clearExitNodes)
    .command(rotateServerSecret)
    .command(clearLicenseKeys)
    .command(deleteClient)
    .command(generateOrgCaKeys)
    .demandCommand()
    .help().argv;


================================================
FILE: cli/wrapper.sh
================================================
#!/bin/sh
cd /app/
./dist/cli.mjs "$@"


================================================
FILE: components.json
================================================
{
    "$schema": "https://ui.shadcn.com/schema.json",
    "style": "default",
    "rsc": true,
    "tsx": true,
    "tailwind": {
        "config": "tailwind.config.ts",
        "css": "src/app/globals.css",
        "baseColor": "neutral",
        "cssVariables": true,
        "prefix": ""
    },
    "aliases": {
        "components": "@/components",
        "utils": "@/lib/utils",
        "ui": "@/components/ui",
        "lib": "@/lib",
        "hooks": "@/hooks"
    }
}


================================================
FILE: config/.gitkeep
================================================


================================================
FILE: config/config.example.yml
================================================
# To see all available options, please visit the docs:
# https://docs.pangolin.net/

gerbil:
    start_port: 51820
    base_endpoint: "{{.DashboardDomain}}"

app:
    dashboard_url: "https://{{.DashboardDomain}}"
    log_level: "info"
    telemetry:
        anonymous_usage: true

domains:
    domain1:
        base_domain: "{{.BaseDomain}}"

server:
    secret: "{{.Secret}}"
    cors:
        origins: ["https://{{.DashboardDomain}}"]
        methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
        allowed_headers: ["X-CSRF-Token", "Content-Type"]
        credentials: false

flags:
    require_email_verification: false
    disable_signup_without_invite: true
    disable_user_create_org: false
    allow_raw_resources: true


================================================
FILE: config/db/.gitkeep
================================================


================================================
FILE: config/logs/.gitkeep
================================================


================================================
FILE: config/traefik/dynamic_config.yml
================================================
http:
  middlewares:
    badger:
      plugin:
        badger:
          disableForwardAuth: true
    redirect-to-https:
      redirectScheme:
        scheme: https

  routers:
    # HTTP to HTTPS redirect router
    main-app-router-redirect:
      rule: "Host(`{{.DashboardDomain}}`)"
      service: next-service
      entryPoints:
        - web
      middlewares:
        - redirect-to-https
        - badger

    # Next.js router (handles everything except API and WebSocket paths)
    next-router:
      rule: "Host(`{{.DashboardDomain}}`) && !PathPrefix(`/api/v1`)"
      service: next-service
      entryPoints:
        - websecure
      middlewares:
        - badger
      tls:
        certResolver: letsencrypt

    # API router (handles /api/v1 paths)
    api-router:
      rule: "Host(`{{.DashboardDomain}}`) && PathPrefix(`/api/v1`)"
      service: api-service
      entryPoints:
        - websecure
      middlewares:
        - badger
      tls:
        certResolver: letsencrypt

  services:
    next-service:
      loadBalancer:
        servers:
          - url: "http://pangolin:3002"  # Next.js server

    api-service:
      loadBalancer:
        servers:
          - url: "http://pangolin:3000"  # API/WebSocket server

tcp:
  serversTransports:
    pp-transport-v1:
      proxyProtocol:
        version: 1
    pp-transport-v2:
      proxyProtocol:
        version: 2


================================================
FILE: config/traefik/traefik_config.yml
================================================
api:
  insecure: true
  dashboard: true

providers:
  http:
    endpoint: "http://pangolin:3001/api/v1/traefik-config"
    pollInterval: "5s"
  file:
    filename: "/etc/traefik/dynamic_config.yml"

experimental:
  plugins:
    badger:
      moduleName: "github.com/fosrl/badger"
      version: "{{.BadgerVersion}}"

log:
  level: "INFO"
  format: "common"
  maxSize: 100
  maxBackups: 3
  maxAge: 3
  compress: true

certificatesResolvers:
  letsencrypt:
    acme:
      httpChallenge:
        entryPoint: web
      email: "{{.LetsEncryptEmail}}"
      storage: "/letsencrypt/acme.json"
      caServer: "https://acme-v02.api.letsencrypt.org/directory"

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
    transport:
      respondingTimeouts:
        readTimeout: "30m"
    http:
      tls:
        certResolver: "letsencrypt"
      encodedCharacters:
        allowEncodedSlash: true
        allowEncodedQuestionMark: true

serversTransport:
  insecureSkipVerify: true

ping:
  entryPoint: "web"


================================================
FILE: crowdin.yml
================================================
files:
  - source: /messages/en-US.json
    translation: /messages/%locale%.json

================================================
FILE: docker-compose.drizzle.yml
================================================
services:
  drizzle-gateway:
    image: ghcr.io/drizzle-team/gateway:latest
    ports:
      - "4984:4983"
    depends_on:
      - db
    environment:
      - STORE_PATH=/app
      - DATABASE_URL=postgresql://postgres:password@db:5432/postgres
    volumes:
      - drizzle-gateway-data:/app

volumes:
  drizzle-gateway-data:


================================================
FILE: docker-compose.example.yml
================================================
name: pangolin
services:
  pangolin:
    image: fosrl/pangolin:latest
    container_name: pangolin
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 1g
        reservations:
          memory: 256m
    volumes:
      - ./config:/app/config
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
      interval: "3s"
      timeout: "3s"
      retries: 15

  gerbil:
    image: fosrl/gerbil:latest
    container_name: gerbil
    restart: unless-stopped
    depends_on:
      pangolin:
        condition: service_healthy
    command:
      - --reachableAt=http://gerbil:3004
      - --generateAndSaveKeyTo=/var/config/key
      - --remoteConfig=http://pangolin:3001/api/v1/
    volumes:
      - ./config/:/var/config
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    ports:
      - 51820:51820/udp
      - 21820:21820/udp
      - 443:443 # Port for traefik because of the network_mode
      - 80:80 # Port for traefik because of the network_mode

  traefik:
    image: traefik:v3.6
    container_name: traefik
    restart: unless-stopped
    network_mode: service:gerbil # Ports appear on the gerbil service
    depends_on:
      pangolin:
        condition: service_healthy
    command:
      - --configFile=/etc/traefik/traefik_config.yml
    volumes:
      - ./config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration
      - ./config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates

networks:
  default:
    driver: bridge
    name: pangolin
    enable_ipv6: true


================================================
FILE: docker-compose.pgr.yml
================================================
services:
  # PostgreSQL Service
  db:
    image: postgres:17 # Use the PostgreSQL 17 image
    container_name: dev_postgres # Name your PostgreSQL container
    environment:
      POSTGRES_DB: postgres # Default database name
      POSTGRES_USER: postgres # Default user
      POSTGRES_PASSWORD: password # Default password (change for production!)
    # volumes:
    #   - ./config/postgres:/var/lib/postgresql/data
    ports:
      - "5432:5432" # Map host port 5432 to container port 5432
    restart: no

  redis:
    image: redis:latest # Use the latest Redis image
    container_name: dev_redis # Name your Redis container
    ports:
      - "6379:6379" # Map host port 6379 to container port 6379
    restart: no


================================================
FILE: docker-compose.yml
================================================
services:
  # Development application service
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    container_name: dev_pangolin
    ports:
      - "3000:3000"
      - "3001:3001"
      - "3002:3002"
      - "3003:3003"
    environment:
      - NODE_ENV=development
      - ENVIRONMENT=dev
    volumes:
      # Mount source code for hot reload
      - ./src:/app/src
      - ./server:/app/server
      - ./public:/app/public
      - ./messages:/app/messages
      - ./components.json:/app/components.json
      - ./next.config.mjs:/app/next.config.mjs
      - ./tsconfig.json:/app/tsconfig.json
      - ./tailwind.config.js:/app/tailwind.config.js
      - ./postcss.config.mjs:/app/postcss.config.mjs
      - ./eslint.config.js:/app/eslint.config.js
      - ./config:/app/config
    restart: no


================================================
FILE: drizzle.pg.config.ts
================================================
import { defineConfig } from "drizzle-kit";
import path from "path";

const schema = [path.join("server", "db", "pg", "schema")];

export default defineConfig({
    dialect: "postgresql",
    schema: schema,
    out: path.join("server", "migrations"),
    verbose: true,
    dbCredentials: {
        url: process.env.DATABASE_URL as string
    }
});


================================================
FILE: drizzle.sqlite.config.ts
================================================
import { APP_PATH } from "@server/lib/consts";
import { defineConfig } from "drizzle-kit";
import path from "path";

const schema = [path.join("server", "db", "sqlite", "schema")];

export default defineConfig({
    dialect: "sqlite",
    schema: schema,
    out: path.join("server", "migrations"),
    verbose: true,
    dbCredentials: {
        url: path.join(APP_PATH, "db", "db.sqlite")
    }
});


================================================
FILE: esbuild.mjs
================================================
import esbuild from "esbuild";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { nodeExternalsPlugin } from "esbuild-node-externals";
import path from "path";
import fs from "fs";
// import { glob } from "glob";

// Read default build type from server/build.ts
let build = "oss";
const buildFile = fs.readFileSync(path.resolve("server/build.ts"), "utf8");
const m = buildFile.match(/export\s+const\s+build\s*=\s*["'](oss|saas|enterprise)["']/);
if (m) build = m[1];

const banner = `
// patch __dirname
// import { fileURLToPath } from "url";
// import path from "path";
// const __filename = fileURLToPath(import.meta.url);
// const __dirname = path.dirname(__filename);

// allow top level await
import { createRequire as topLevelCreateRequire } from "module";
const require = topLevelCreateRequire(import.meta.url);
`;

const argv = yargs(hideBin(process.argv))
    .usage("Usage: $0 -entry [string] -out [string] -build [string]")
    .option("entry", {
        alias: "e",
        describe: "Entry point file",
        type: "string",
        demandOption: true
    })
    .option("out", {
        alias: "o",
        describe: "Output file path",
        type: "string",
        demandOption: true
    })
    .option("build", {
        alias: "b",
        describe: "Build type (oss, saas, enterprise)",
        type: "string",
        choices: ["oss", "saas", "enterprise"],
        default: build
    })
    .help()
    .alias("help", "h").argv;

// generate a list of all package.json files in the monorepo
function getPackagePaths() {
    // const packagePaths = [];
    // const packageGlob = "package.json";
    // const packageJsonFiles = glob.sync(packageGlob);
    // for (const packageJsonFile of packageJsonFiles) {
    //     packagePaths.push(path.dirname(packageJsonFile) + "/package.json");
    // }
    // return packagePaths;
    return ["package.json"];
}

// Plugin to guard against bad imports from #private
function privateImportGuardPlugin() {
    return {
        name: "private-import-guard",
        setup(build) {
            const violations = [];

            build.onResolve({ filter: /^#private\// }, (args) => {
                const importingFile = args.importer;

                // Check if the importing file is NOT in server/private
                const normalizedImporter = path.normalize(importingFile);
                const isInServerPrivate = normalizedImporter.includes(
                    path.normalize("server/private")
                );

                if (!isInServerPrivate) {
                    const violation = {
                        file: importingFile,
                        importPath: args.path,
                        resolveDir: args.resolveDir
                    };
                    violations.push(violation);

                    console.log(`PRIVATE IMPORT VIOLATION:`);
                    console.log(`   File: ${importingFile}`);
                    console.log(`   Import: ${args.path}`);
                    console.log(`   Resolve dir: ${args.resolveDir || "N/A"}`);
                    console.log("");
                }

                // Return null to let the default resolver handle it
                return null;
            });

            build.onEnd((result) => {
                if (violations.length > 0) {
                    console.log(
                        `\nSUMMARY: Found ${violations.length} private import violation(s):`
                    );
                    violations.forEach((v, i) => {
                        console.log(
                            `   ${i + 1}. ${path.relative(process.cwd(), v.file)} imports ${v.importPath}`
                        );
                    });
                    console.log("");

                    result.errors.push({
                        text: `Private import violations detected: ${violations.length} violation(s) found`,
                        location: null,
                        notes: violations.map((v) => ({
                            text: `${path.relative(process.cwd(), v.file)} imports ${v.importPath}`,
                            location: null
                        }))
                    });
                }
            });
        }
    };
}

// Plugin to guard against bad imports from #private
function dynamicImportGuardPlugin() {
    return {
        name: "dynamic-import-guard",
        setup(build) {
            const violations = [];

            build.onResolve({ filter: /^#dynamic\// }, (args) => {
                const importingFile = args.importer;

                // Check if the importing file is NOT in server/private
                const normalizedImporter = path.normalize(importingFile);
                const isInServerPrivate = normalizedImporter.includes(
                    path.normalize("server/private")
                );

                if (isInServerPrivate) {
                    const violation = {
                        file: importingFile,
                        importPath: args.path,
                        resolveDir: args.resolveDir
                    };
                    violations.push(violation);

                    console.log(`DYNAMIC IMPORT VIOLATION:`);
                    console.log(`   File: ${importingFile}`);
                    console.log(`   Import: ${args.path}`);
                    console.log(`   Resolve dir: ${args.resolveDir || "N/A"}`);
                    console.log("");
                }

                // Return null to let the default resolver handle it
                return null;
            });

            build.onEnd((result) => {
                if (violations.length > 0) {
                    console.log(
                        `\nSUMMARY: Found ${violations.length} dynamic import violation(s):`
                    );
                    violations.forEach((v, i) => {
                        console.log(
                            `   ${i + 1}. ${path.relative(process.cwd(), v.file)} imports ${v.importPath}`
                        );
                    });
                    console.log("");

                    result.errors.push({
                        text: `Dynamic import violations detected: ${violations.length} violation(s) found`,
                        location: null,
                        notes: violations.map((v) => ({
                            text: `${path.relative(process.cwd(), v.file)} imports ${v.importPath}`,
                            location: null
                        }))
                    });
                }
            });
        }
    };
}

// Plugin to dynamically switch imports based on build type
function dynamicImportSwitcherPlugin(buildValue) {
    return {
        name: "dynamic-import-switcher",
        setup(build) {
            const switches = [];

            build.onStart(() => {
                console.log(
                    `Dynamic import switcher using build type: ${buildValue}`
                );
            });

            build.onResolve({ filter: /^#dynamic\// }, (args) => {
                // Extract the path after #dynamic/
                const dynamicPath = args.path.replace(/^#dynamic\//, "");

                // Determine the replacement based on build type
                let replacement;
                if (buildValue === "oss") {
                    replacement = `#open/${dynamicPath}`;
                } else if (
                    buildValue === "saas" ||
                    buildValue === "enterprise"
                ) {
                    replacement = `#closed/${dynamicPath}`; // We use #closed here so that the route guards dont complain after its been changed but this is the same as #private
                } else {
                    console.warn(
                        `Unknown build type '${buildValue}', defaulting to #open/`
                    );
                    replacement = `#open/${dynamicPath}`;
                }

                const switchInfo = {
                    file: args.importer,
                    originalPath: args.path,
                    replacementPath: replacement,
                    buildType: buildValue
                };
                switches.push(switchInfo);

                console.log(`DYNAMIC IMPORT SWITCH:`);
                console.log(`   File: ${args.importer}`);
                console.log(`   Original: ${args.path}`);
                console.log(
                    `   Switched to: ${replacement} (build: ${buildValue})`
                );
                console.log("");

                // Rewrite the import path and let the normal resolution continue
                return build.resolve(replacement, {
                    importer: args.importer,
                    namespace: args.namespace,
                    resolveDir: args.resolveDir,
                    kind: args.kind
                });
            });

            build.onEnd((result) => {
                if (switches.length > 0) {
                    console.log(
                        `\nDYNAMIC IMPORT SUMMARY: Switched ${switches.length} import(s) for build type '${buildValue}':`
                    );
                    switches.forEach((s, i) => {
                        console.log(
                            `   ${i + 1}. ${path.relative(process.cwd(), s.file)}`
                        );
                        console.log(
                            `      ${s.originalPath} → ${s.replacementPath}`
                        );
                    });
                    console.log("");
                }
            });
        }
    };
}

esbuild
    .build({
        entryPoints: [argv.entry],
        bundle: true,
        outfile: argv.out,
        format: "esm",
        minify: false,
        banner: {
            js: banner
        },
        platform: "node",
        external: ["body-parser"],
        plugins: [
            privateImportGuardPlugin(),
            dynamicImportGuardPlugin(),
            dynamicImportSwitcherPlugin(argv.build),
            nodeExternalsPlugin({
                packagePath: getPackagePaths()
            })
        ],
        sourcemap: "inline",
        target: "node24"
    })
    .then((result) => {
        // Check if there were any errors in the build result
        if (result.errors && result.errors.length > 0) {
            console.error(
                `Build failed with ${result.errors.length} error(s):`
            );
            result.errors.forEach((error, i) => {
                console.error(`${i + 1}. ${error.text}`);
                if (error.notes) {
                    error.notes.forEach((note) => {
                        console.error(`   - ${note.text}`);
                    });
                }
            });

            // remove the output file if it was created
            if (fs.existsSync(argv.out)) {
                fs.unlinkSync(argv.out);
            }

            process.exit(1);
        }

        console.log("Build completed successfully");
    })
    .catch((error) => {
        console.error("Build failed:", error);
        process.exit(1);
    });


================================================
FILE: eslint.config.js
================================================
import tseslint from "typescript-eslint";

export default tseslint.config({
    files: ["**/*.{ts,tsx,js,jsx}"],
    languageOptions: {
        parser: tseslint.parser,
        parserOptions: {
            ecmaVersion: "latest",
            sourceType: "module",
            ecmaFeatures: {
                jsx: true
            }
        }
    },
    rules: {
        semi: "error",
        "prefer-const": "warn"
    }
});


================================================
FILE: install/Makefile
================================================
all: go-build-release

# Build with version injection via ldflags
# Versions can be passed via: make go-build-release PANGOLIN_VERSION=x.x.x GERBIL_VERSION=x.x.x BADGER_VERSION=x.x.x
# Or fetched automatically if not provided (requires curl and jq)

PANGOLIN_VERSION ?= $(shell curl -s https://api.github.com/repos/fosrl/pangolin/tags | jq -r '.[0].name')
GERBIL_VERSION ?= $(shell curl -s https://api.github.com/repos/fosrl/gerbil/tags | jq -r '.[0].name')
BADGER_VERSION ?= $(shell curl -s https://api.github.com/repos/fosrl/badger/tags | jq -r '.[0].name')

LDFLAGS = -X main.pangolinVersion=$(PANGOLIN_VERSION) \
          -X main.gerbilVersion=$(GERBIL_VERSION) \
          -X main.badgerVersion=$(BADGER_VERSION)

go-build-release:
	@echo "Building with versions - Pangolin: $(PANGOLIN_VERSION), Gerbil: $(GERBIL_VERSION), Badger: $(BADGER_VERSION)"
	CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o bin/installer_linux_amd64
	CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o bin/installer_linux_arm64

clean:
	rm -f bin/installer_linux_amd64
	rm -f bin/installer_linux_arm64

.PHONY: all go-build-release clean


================================================
FILE: install/config/config.yml
================================================
# To see all available options, please visit the docs:
# https://docs.pangolin.net/

gerbil:
    start_port: 51820
    base_endpoint: "{{.DashboardDomain}}"

app:
    dashboard_url: "https://{{.DashboardDomain}}"
    log_level: "info"
    telemetry:
        anonymous_usage: true

domains:
    domain1:
        base_domain: "{{.BaseDomain}}"

server:
    secret: "{{.Secret}}"
    cors:
        origins: ["https://{{.DashboardDomain}}"]
        methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
        allowed_headers: ["X-CSRF-Token", "Content-Type"]
        credentials: false
    {{if .EnableGeoblocking}}maxmind_db_path: "./config/GeoLite2-Country.mmdb"{{end}}
{{if .EnableEmail}}
email:
    smtp_host: "{{.EmailSMTPHost}}"
    smtp_port: {{.EmailSMTPPort}}
    smtp_user: "{{.EmailSMTPUser}}"
    smtp_pass: "{{.EmailSMTPPass}}"
    no_reply: "{{.EmailNoReply}}"
{{end}}
flags:
    require_email_verification: {{.EnableEmail}}
    disable_signup_without_invite: true
    disable_user_create_org: false
    allow_raw_resources: true


================================================
FILE: install/config/crowdsec/acquis.d/appsec.yaml
================================================
listen_addr: 0.0.0.0:7422
appsec_config: crowdsecurity/appsec-default
name: myAppSecComponent
source: appsec
labels:
  type: appsec


================================================
FILE: install/config/crowdsec/acquis.d/traefik.yaml
================================================
poll_without_inotify: false
filenames:
  - /var/log/traefik/*.log
labels:
  type: traefik


================================================
FILE: install/config/crowdsec/docker-compose.yml
================================================
services:
  crowdsec:
    image: docker.io/crowdsecurity/crowdsec:latest
    container_name: crowdsec
    environment:
      GID: "1000"
      COLLECTIONS: crowdsecurity/traefik crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-generic-rules
      ENROLL_INSTANCE_NAME: "pangolin-crowdsec"
      PARSERS: crowdsecurity/whitelists
      ENROLL_TAGS: docker
    healthcheck:
        test:
            - CMD
            - cscli
            - lapi
            - status
        interval: 10s
        timeout: 5s
        retries: 3
        start_period: 30s
    labels:
      - "traefik.enable=false" # Disable traefik for crowdsec
    volumes:
      # crowdsec container data
      - ./config/crowdsec:/etc/crowdsec # crowdsec config
      - ./config/crowdsec/db:/var/lib/crowdsec/data # crowdsec db
      # log bind mounts into crowdsec
      - ./config/traefik/logs:/var/log/traefik # traefik logs
    ports:
      - 6060:6060 # metrics endpoint for prometheus
    restart: unless-stopped
    command: -t # Add test config flag to verify configuration


================================================
FILE: install/config/crowdsec/dynamic_config.yml
================================================
http:
  middlewares:
    badger:
      plugin:
        badger:
          disableForwardAuth: true
    redirect-to-https:
      redirectScheme:
        scheme: https
    default-whitelist: # Whitelist middleware for internal IPs
      ipWhiteList:  # Internal IP addresses
        sourceRange:  # Internal IP addresses
        - "10.0.0.0/8"  # Internal IP addresses
        - "192.168.0.0/16" # Internal IP addresses
        - "172.16.0.0/12" # Internal IP addresses
    # Basic security headers
    security-headers:
      headers:
        customResponseHeaders:  # Custom response headers
          Server: "" # Remove server header
          X-Powered-By: "" # Remove powered by header
          X-Forwarded-Proto: "https"  # Set forwarded proto to https
        sslProxyHeaders: # SSL proxy headers
          X-Forwarded-Proto: "https" # Set forwarded proto to https
        hostsProxyHeaders: # Hosts proxy headers
          - "X-Forwarded-Host" # Set forwarded host
        contentTypeNosniff: true # Prevent MIME sniffing
        customFrameOptionsValue: "SAMEORIGIN" # Set frame options
        referrerPolicy: "strict-origin-when-cross-origin" # Set referrer policy
        forceSTSHeader: true # Force STS header
        stsIncludeSubdomains: true # Include subdomains
        stsSeconds: 63072000 # STS seconds
        stsPreload: true # Preload STS
    # CrowdSec configuration with proper IP forwarding
    crowdsec:
      plugin:
        crowdsec:
          enabled: true # Enable CrowdSec plugin
          logLevel: INFO # Log level
          updateIntervalSeconds: 15 # Update interval
          updateMaxFailure: 0 # Update max failure
          defaultDecisionSeconds: 15 # Default decision seconds
          httpTimeoutSeconds: 10 # HTTP timeout
          crowdsecMode: live # CrowdSec mode
          crowdsecAppsecEnabled: true # Enable AppSec
          crowdsecAppsecHost: crowdsec:7422 # CrowdSec IP address which you noted down later
          crowdsecAppsecFailureBlock: true # Block on failure
          crowdsecAppsecUnreachableBlock: true # Block on unreachable
          crowdsecAppsecBodyLimit: 10485760
          crowdsecLapiKey: "PUT_YOUR_BOUNCER_KEY_HERE_OR_IT_WILL_NOT_WORK" # CrowdSec API key which you noted down later
          crowdsecLapiHost: crowdsec:8080 # CrowdSec
          crowdsecLapiScheme: http # CrowdSec API scheme
          forwardedHeadersTrustedIPs: # Forwarded headers trusted IPs
            - "0.0.0.0/0" # All IP addresses are trusted for forwarded headers (CHANGE MADE HERE)
          clientTrustedIPs: # Client trusted IPs (CHANGE MADE HERE)
            - "10.0.0.0/8" # Internal LAN IP addresses
            - "172.16.0.0/12" # Internal LAN IP addresses
            - "192.168.0.0/16" # Internal LAN IP addresses
            - "100.89.137.0/20" # Internal LAN IP addresses

  routers:
    # HTTP to HTTPS redirect router
    main-app-router-redirect:
      rule: "Host(`{{.DashboardDomain}}`)" # Dynamic Domain Name
      service: next-service
      entryPoints:
        - web
      middlewares:
        - redirect-to-https
        - badger

    # Next.js router (handles everything except API and WebSocket paths)
    next-router:
      rule: "Host(`{{.DashboardDomain}}`) && !PathPrefix(`/api/v1`)" # Dynamic Domain Name
      service: next-service
      entryPoints:
        - websecure
      middlewares:
        - security-headers # Add security headers middleware
        - badger
      tls:
        certResolver: letsencrypt

    # API router (handles /api/v1 paths)
    api-router:
      rule: "Host(`{{.DashboardDomain}}`) && PathPrefix(`/api/v1`)" # Dynamic Domain Name
      service: api-service
      entryPoints:
        - websecure
      middlewares:
        - security-headers # Add security headers middleware
        - badger
      tls:
        certResolver: letsencrypt

    # WebSocket router
    ws-router:
      rule: "Host(`{{.DashboardDomain}}`)" # Dynamic Domain Name
      service: api-service
      entryPoints:
        - websecure
      middlewares:
        - security-headers # Add security headers middleware
        - badger
      tls:
        certResolver: letsencrypt

  services:
    next-service:
      loadBalancer:
        servers:
          - url: "http://pangolin:3002"  # Next.js server

    api-service:
      loadBalancer:
        servers:
          - url: "http://pangolin:3000"  # API/WebSocket server

tcp:
  serversTransports:
    pp-transport-v1:
      proxyProtocol:
        version: 1
    pp-transport-v2:
      proxyProtocol:
        version: 2


================================================
FILE: install/config/crowdsec/profiles.yaml
================================================
name: captcha_remediation
filters:
  - Alert.Remediation == true && Alert.GetScope() == "Ip" && Alert.GetScenario() contains "http"
decisions:
  - type: captcha
    duration: 4h
on_success: break

---
name: default_ip_remediation
filters:
 - Alert.Remediation == true && Alert.GetScope() == "Ip"
decisions:
 - type: ban
   duration: 4h
on_success: break

---
name: default_range_remediation
filters:
 - Alert.Remediation == true && Alert.GetScope() == "Range"
decisions:
 - type: ban
   duration: 4h
on_success: break


================================================
FILE: install/config/crowdsec/traefik_config.yml
================================================
api:
  insecure: true
  dashboard: true

providers:
  http:
    endpoint: "http://pangolin:3001/api/v1/traefik-config"
    pollInterval: "5s"
  file:
    filename: "/etc/traefik/dynamic_config.yml"

experimental:
  plugins:
    badger:
      moduleName: "github.com/fosrl/badger"
      version: "{{.BadgerVersion}}"
    crowdsec: # CrowdSec plugin configuration added
      moduleName: "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
      version: "v1.4.4"

log:
  level: "INFO"
  format: "json" # Log format changed to json for better parsing
  maxSize: 100
  maxBackups: 3
  maxAge: 3
  compress: true

accessLog: # We enable access logs as json
  filePath: "/var/log/traefik/access.log"
  format: json
  filters:
    statusCodes:
      - "200-299"  # Success codes
      - "400-499"  # Client errors
      - "500-599"  # Server errors
    retryAttempts: true
    minDuration: "100ms"  # Increased to focus on slower requests
  bufferingSize: 100      # Add buffering for better performance
  fields:
    defaultMode: drop     # Start with dropping all fields
    names:
      ClientAddr: keep # Keep client address for IP tracking
      ClientHost: keep  # Keep client host for IP tracking
      RequestMethod: keep # Keep request method for tracking
      RequestPath: keep # Keep request path for tracking
      RequestProtocol: keep # Keep request protocol for tracking
      DownstreamStatus: keep # Keep downstream status for tracking
      DownstreamContentSize: keep # Keep downstream content size for tracking
      Duration: keep # Kee
Download .txt
gitextract_nwjdm6gt/

├── .dockerignore
├── .editorconfig
├── .eslintrc.json
├── .github/
│   ├── DISCUSSION_TEMPLATE/
│   │   └── feature-requests.yml
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── 1.bug_report.yml
│   │   └── config.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── cicd.yml
│       ├── linting.yml
│       ├── mirror.yaml
│       ├── restart-runners.yml
│       ├── saas.yml
│       ├── stale-bot.yml
│       └── test.yml
├── .gitignore
├── .nvmrc
├── .prettierignore
├── .prettierrc
├── .vscode/
│   ├── extensions.json
│   └── settings.json
├── CONTRIBUTING.md
├── Dockerfile
├── Dockerfile.dev
├── LICENSE
├── Makefile
├── README.md
├── SECURITY.md
├── bruno/
│   ├── API Keys/
│   │   ├── Create API Key.bru
│   │   ├── Delete API Key.bru
│   │   ├── List API Key Actions.bru
│   │   ├── List Org API Keys.bru
│   │   ├── List Root API Keys.bru
│   │   ├── Set API Key Actions.bru
│   │   ├── Set API Key Orgs.bru
│   │   └── folder.bru
│   ├── Auth/
│   │   ├── 2fa-disable.bru
│   │   ├── 2fa-enable.bru
│   │   ├── 2fa-request.bru
│   │   ├── change-password.bru
│   │   ├── login.bru
│   │   ├── logout.bru
│   │   ├── reset-password-request.bru
│   │   ├── reset-password.bru
│   │   ├── signup.bru
│   │   ├── verify-email-request.bru
│   │   ├── verify-email.bru
│   │   └── verify-user.bru
│   ├── Clients/
│   │   ├── createClient.bru
│   │   └── pickClientDefaults.bru
│   ├── IDP/
│   │   ├── Create OIDC Provider.bru
│   │   ├── Generate OIDC URL.bru
│   │   └── folder.bru
│   ├── Internal/
│   │   ├── Traefik Config.bru
│   │   └── folder.bru
│   ├── Newt/
│   │   ├── Create Newt.bru
│   │   └── Get Token.bru
│   ├── Olm/
│   │   ├── createOlm.bru
│   │   └── folder.bru
│   ├── Orgs/
│   │   ├── Check Id.bru
│   │   └── listOrgs.bru
│   ├── Remote Exit Node/
│   │   └── createRemoteExitNode.bru
│   ├── Resources/
│   │   ├── listResourcesByOrg.bru
│   │   └── listResourcesBySite.bru
│   ├── Sites/
│   │   ├── Get Site.bru
│   │   └── listSites.bru
│   ├── Targets/
│   │   └── listTargets.bru
│   ├── Test.bru
│   ├── Traefik/
│   │   └── traefik-config.bru
│   ├── Users/
│   │   ├── adminListUsers.bru
│   │   ├── adminRemoveUser.bru
│   │   └── getUser.bru
│   └── bruno.json
├── cli/
│   ├── commands/
│   │   ├── clearExitNodes.ts
│   │   ├── clearLicenseKeys.ts
│   │   ├── deleteClient.ts
│   │   ├── generateOrgCaKeys.ts
│   │   ├── resetUserSecurityKeys.ts
│   │   ├── rotateServerSecret.ts
│   │   └── setAdminCredentials.ts
│   ├── index.ts
│   └── wrapper.sh
├── components.json
├── config/
│   ├── .gitkeep
│   ├── config.example.yml
│   ├── db/
│   │   └── .gitkeep
│   ├── logs/
│   │   └── .gitkeep
│   └── traefik/
│       ├── dynamic_config.yml
│       └── traefik_config.yml
├── crowdin.yml
├── docker-compose.drizzle.yml
├── docker-compose.example.yml
├── docker-compose.pgr.yml
├── docker-compose.yml
├── drizzle.pg.config.ts
├── drizzle.sqlite.config.ts
├── esbuild.mjs
├── eslint.config.js
├── install/
│   ├── Makefile
│   ├── config/
│   │   ├── config.yml
│   │   ├── crowdsec/
│   │   │   ├── acquis.d/
│   │   │   │   ├── appsec.yaml
│   │   │   │   └── traefik.yaml
│   │   │   ├── docker-compose.yml
│   │   │   ├── dynamic_config.yml
│   │   │   ├── profiles.yaml
│   │   │   └── traefik_config.yml
│   │   ├── docker-compose.yml
│   │   └── traefik/
│   │       ├── dynamic_config.yml
│   │       └── traefik_config.yml
│   ├── config.go
│   ├── containers.go
│   ├── crowdsec.go
│   ├── get-installer.sh
│   ├── go.mod
│   ├── go.sum
│   ├── input.go
│   ├── input.txt
│   ├── main.go
│   └── theme.go
├── messages/
│   ├── bg-BG.json
│   ├── cs-CZ.json
│   ├── de-DE.json
│   ├── en-US.json
│   ├── es-ES.json
│   ├── fr-FR.json
│   ├── it-IT.json
│   ├── ko-KR.json
│   ├── nb-NO.json
│   ├── nl-NL.json
│   ├── pl-PL.json
│   ├── pt-PT.json
│   ├── ru-RU.json
│   ├── tr-TR.json
│   ├── zh-CN.json
│   └── zh-TW.json
├── next.config.ts
├── package.json
├── postcss.config.mjs
├── server/
│   ├── apiServer.ts
│   ├── auth/
│   │   ├── actions.ts
│   │   ├── canUserAccessResource.ts
│   │   ├── canUserAccessSiteResource.ts
│   │   ├── checkValidInvite.ts
│   │   ├── password.ts
│   │   ├── passwordSchema.ts
│   │   ├── resourceOtp.ts
│   │   ├── sendEmailVerificationCode.ts
│   │   ├── sessions/
│   │   │   ├── app.ts
│   │   │   ├── newt.ts
│   │   │   ├── olm.ts
│   │   │   ├── resource.ts
│   │   │   └── verifySession.ts
│   │   ├── totp.ts
│   │   ├── unauthorizedResponse.ts
│   │   └── verifyResourceAccessToken.ts
│   ├── cleanup.ts
│   ├── db/
│   │   ├── README.md
│   │   ├── asns.ts
│   │   ├── countries.ts
│   │   ├── ios_models.json
│   │   ├── mac_models.json
│   │   ├── maxmind.ts
│   │   ├── maxmindAsn.ts
│   │   ├── migrate.ts
│   │   ├── names.json
│   │   ├── names.ts
│   │   ├── pg/
│   │   │   ├── driver.ts
│   │   │   ├── index.ts
│   │   │   ├── logsDriver.ts
│   │   │   ├── migrate.ts
│   │   │   ├── safeRead.ts
│   │   │   └── schema/
│   │   │       ├── privateSchema.ts
│   │   │       └── schema.ts
│   │   ├── queries/
│   │   │   └── verifySessionQueries.ts
│   │   └── sqlite/
│   │       ├── driver.ts
│   │       ├── index.ts
│   │       ├── logsDriver.ts
│   │       ├── migrate.ts
│   │       ├── safeRead.ts
│   │       └── schema/
│   │           ├── privateSchema.ts
│   │           └── schema.ts
│   ├── emails/
│   │   ├── index.ts
│   │   ├── sendEmail.ts
│   │   └── templates/
│   │       ├── EnterpriseEditionKeyGenerated.tsx
│   │       ├── NotifyResetPassword.tsx
│   │       ├── NotifyUsageLimitApproaching.tsx
│   │       ├── NotifyUsageLimitReached.tsx
│   │       ├── ResetPasswordCode.tsx
│   │       ├── ResourceOTPCode.tsx
│   │       ├── SendInviteLink.tsx
│   │       ├── SupportEmail.tsx
│   │       ├── TwoFactorAuthNotification.tsx
│   │       ├── VerifyEmailCode.tsx
│   │       ├── WelcomeQuickStart.tsx
│   │       ├── components/
│   │       │   ├── ButtonLink.tsx
│   │       │   ├── CopyCodeBox.tsx
│   │       │   └── Email.tsx
│   │       └── lib/
│   │           └── theme.ts
│   ├── extendZod.ts
│   ├── index.ts
│   ├── integrationApiServer.ts
│   ├── internalServer.ts
│   ├── lib/
│   │   ├── asn.ts
│   │   ├── billing/
│   │   │   ├── createCustomer.ts
│   │   │   ├── features.ts
│   │   │   ├── getLineItems.ts
│   │   │   ├── getOrgTierData.ts
│   │   │   ├── index.ts
│   │   │   ├── licenses.ts
│   │   │   ├── limitSet.ts
│   │   │   ├── limitsService.ts
│   │   │   ├── tierMatrix.ts
│   │   │   └── usageService.ts
│   │   ├── blueprints/
│   │   │   ├── MaintenanceSchema.ts
│   │   │   ├── applyBlueprint.ts
│   │   │   ├── applyNewtDockerBlueprint.ts
│   │   │   ├── clientResources.ts
│   │   │   ├── parseDockerContainers.ts
│   │   │   ├── parseDotNotation.ts
│   │   │   ├── proxyResources.ts
│   │   │   └── types.ts
│   │   ├── cache.ts
│   │   ├── calculateUserClientsForOrgs.ts
│   │   ├── canUserAccessResource.ts
│   │   ├── certificates.ts
│   │   ├── checkOrgAccessPolicy.ts
│   │   ├── cleanupLogs.test.ts
│   │   ├── cleanupLogs.ts
│   │   ├── clientVersionChecks.ts
│   │   ├── colorsSchema.ts
│   │   ├── config.ts
│   │   ├── consts.ts
│   │   ├── corsWithLoginPage.ts
│   │   ├── crypto.ts
│   │   ├── deleteOrg.ts
│   │   ├── domainUtils.ts
│   │   ├── encryption.ts
│   │   ├── exitNodes/
│   │   │   ├── exitNodeComms.ts
│   │   │   ├── exitNodes.ts
│   │   │   ├── getCurrentExitNodeId.ts
│   │   │   ├── index.ts
│   │   │   └── subnet.ts
│   │   ├── geoip.ts
│   │   ├── getEnvOrYaml.ts
│   │   ├── hostMeta.ts
│   │   ├── idp/
│   │   │   └── generateRedirectUrl.ts
│   │   ├── ip.test.ts
│   │   ├── ip.ts
│   │   ├── isLicencedOrSubscribed.ts
│   │   ├── isSubscribed.ts
│   │   ├── lock.ts
│   │   ├── logAccessAudit.ts
│   │   ├── normalizePostAuthPath.ts
│   │   ├── rateLimitStore.ts
│   │   ├── readConfigFile.ts
│   │   ├── rebuildClientAssociations.ts
│   │   ├── response.ts
│   │   ├── s3.ts
│   │   ├── schemas.ts
│   │   ├── serverIpService.ts
│   │   ├── sshCA.ts
│   │   ├── stoi.ts
│   │   ├── telemetry.ts
│   │   ├── totp.ts
│   │   ├── traefik/
│   │   │   ├── TraefikConfigManager.ts
│   │   │   ├── getTraefikConfig.ts
│   │   │   ├── index.ts
│   │   │   ├── middleware.ts
│   │   │   ├── pathEncoding.test.ts
│   │   │   ├── traefikConfig.test.ts
│   │   │   └── utils.ts
│   │   ├── userOrg.ts
│   │   ├── validators.test.ts
│   │   └── validators.ts
│   ├── license/
│   │   └── license.ts
│   ├── logger.ts
│   ├── middlewares/
│   │   ├── csrfProtection.ts
│   │   ├── formatError.ts
│   │   ├── getUserOrgs.ts
│   │   ├── index.ts
│   │   ├── integration/
│   │   │   ├── index.ts
│   │   │   ├── verifyAccessTokenAccess.ts
│   │   │   ├── verifyApiKey.ts
│   │   │   ├── verifyApiKeyApiKeyAccess.ts
│   │   │   ├── verifyApiKeyClientAccess.ts
│   │   │   ├── verifyApiKeyDomainAccess.ts
│   │   │   ├── verifyApiKeyHasAction.ts
│   │   │   ├── verifyApiKeyIdpAccess.ts
│   │   │   ├── verifyApiKeyIsRoot.ts
│   │   │   ├── verifyApiKeyOrgAccess.ts
│   │   │   ├── verifyApiKeyResourceAccess.ts
│   │   │   ├── verifyApiKeyRoleAccess.ts
│   │   │   ├── verifyApiKeySetResourceClients.ts
│   │   │   ├── verifyApiKeySetResourceUsers.ts
│   │   │   ├── verifyApiKeySiteAccess.ts
│   │   │   ├── verifyApiKeySiteResourceAccess.ts
│   │   │   ├── verifyApiKeyTargetAccess.ts
│   │   │   └── verifyApiKeyUserAccess.ts
│   │   ├── logActionAudit.ts
│   │   ├── logIncoming.ts
│   │   ├── notFound.ts
│   │   ├── requestTimeout.ts
│   │   ├── stripDuplicateSessions.ts
│   │   ├── verifyAccessTokenAccess.ts
│   │   ├── verifyAdmin.ts
│   │   ├── verifyApiKeyAccess.ts
│   │   ├── verifyClientAccess.ts
│   │   ├── verifyDomainAccess.ts
│   │   ├── verifyIsLoggedInUser.ts
│   │   ├── verifyLimits.ts
│   │   ├── verifyOlmAccess.ts
│   │   ├── verifyOrgAccess.ts
│   │   ├── verifyResourceAccess.ts
│   │   ├── verifyRoleAccess.ts
│   │   ├── verifySession.ts
│   │   ├── verifySetResourceClients.ts
│   │   ├── verifySetResourceUsers.ts
│   │   ├── verifySiteAccess.ts
│   │   ├── verifySiteResourceAccess.ts
│   │   ├── verifyTargetAccess.ts
│   │   ├── verifyUser.ts
│   │   ├── verifyUserAccess.ts
│   │   ├── verifyUserHasAction.ts
│   │   ├── verifyUserInRole.ts
│   │   ├── verifyUserIsOrgOwner.ts
│   │   └── verifyUserIsServerAdmin.ts
│   ├── nextServer.ts
│   ├── openApi.ts
│   ├── private/
│   │   ├── auth/
│   │   │   └── sessions/
│   │   │       └── remoteExitNode.ts
│   │   ├── cleanup.ts
│   │   ├── lib/
│   │   │   ├── billing/
│   │   │   │   ├── createCustomer.ts
│   │   │   │   ├── getOrgTierData.ts
│   │   │   │   └── index.ts
│   │   │   ├── blueprints/
│   │   │   │   └── MaintenanceSchema.ts
│   │   │   ├── cache.ts
│   │   │   ├── certificates.ts
│   │   │   ├── checkOrgAccessPolicy.ts
│   │   │   ├── config.ts
│   │   │   ├── exitNodes/
│   │   │   │   ├── exitNodeComms.ts
│   │   │   │   ├── exitNodes.ts
│   │   │   │   └── index.ts
│   │   │   ├── isLicencedOrSubscribed.ts
│   │   │   ├── isSubscribed.ts
│   │   │   ├── lock.ts
│   │   │   ├── logAccessAudit.ts
│   │   │   ├── rateLimit.test.ts
│   │   │   ├── rateLimit.ts
│   │   │   ├── rateLimitStore.ts
│   │   │   ├── readConfigFile.ts
│   │   │   ├── redis.ts
│   │   │   ├── redisStore.ts
│   │   │   ├── stripe.ts
│   │   │   └── traefik/
│   │   │       ├── getTraefikConfig.ts
│   │   │       └── index.ts
│   │   ├── license/
│   │   │   ├── license.ts
│   │   │   └── licenseJwt.ts
│   │   ├── middlewares/
│   │   │   ├── index.ts
│   │   │   ├── logActionAudit.ts
│   │   │   ├── verifyCertificateAccess.ts
│   │   │   ├── verifyIdpAccess.ts
│   │   │   ├── verifyLoginPageAccess.ts
│   │   │   ├── verifyRemoteExitNode.ts
│   │   │   ├── verifyRemoteExitNodeAccess.ts
│   │   │   ├── verifySubscription.ts
│   │   │   └── verifyValidLicense.ts
│   │   └── routers/
│   │       ├── approvals/
│   │       │   ├── countApprovals.ts
│   │       │   ├── index.ts
│   │       │   ├── listApprovals.ts
│   │       │   └── processPendingApproval.ts
│   │       ├── auditLogs/
│   │       │   ├── exportAccessAuditLog.ts
│   │       │   ├── exportActionAuditLog.ts
│   │       │   ├── index.ts
│   │       │   ├── queryAccessAuditLog.ts
│   │       │   └── queryActionAuditLog.ts
│   │       ├── auth/
│   │       │   ├── getSessionTransferToken.ts
│   │       │   ├── index.ts
│   │       │   └── transferSession.ts
│   │       ├── billing/
│   │       │   ├── changeTier.ts
│   │       │   ├── createCheckoutSession.ts
│   │       │   ├── createPortalSession.ts
│   │       │   ├── featureLifecycle.ts
│   │       │   ├── getOrgSubscriptions.ts
│   │       │   ├── getOrgUsage.ts
│   │       │   ├── hooks/
│   │       │   │   ├── getSubType.ts
│   │       │   │   ├── handleCustomerCreated.ts
│   │       │   │   ├── handleCustomerDeleted.ts
│   │       │   │   ├── handleCustomerUpdated.ts
│   │       │   │   ├── handleSubscriptionCreated.ts
│   │       │   │   ├── handleSubscriptionDeleted.ts
│   │       │   │   └── handleSubscriptionUpdated.ts
│   │       │   ├── index.ts
│   │       │   ├── internalGetOrgTier.ts
│   │       │   ├── subscriptionLifecycle.ts
│   │       │   └── webhooks.ts
│   │       ├── certificates/
│   │       │   ├── createCertificate.ts
│   │       │   ├── getCertificate.ts
│   │       │   ├── index.ts
│   │       │   └── restartCertificate.ts
│   │       ├── domain/
│   │       │   ├── checkDomainNamespaceAvailability.ts
│   │       │   ├── index.ts
│   │       │   └── listDomainNamespaces.ts
│   │       ├── external.ts
│   │       ├── generatedLicense/
│   │       │   ├── generateNewEnterpriseLicense.ts
│   │       │   ├── generateNewLicense.ts
│   │       │   ├── index.ts
│   │       │   └── listGeneratedLicenses.ts
│   │       ├── gerbil/
│   │       │   └── createExitNode.ts
│   │       ├── hybrid.ts
│   │       ├── integration.ts
│   │       ├── internal.ts
│   │       ├── license/
│   │       │   ├── activateLicense.ts
│   │       │   ├── deleteLicenseKey.ts
│   │       │   ├── getLicenseStatus.ts
│   │       │   ├── index.ts
│   │       │   ├── listLicenseKeys.ts
│   │       │   └── recheckStatus.ts
│   │       ├── loginPage/
│   │       │   ├── createLoginPage.ts
│   │       │   ├── deleteLoginPage.ts
│   │       │   ├── deleteLoginPageBranding.ts
│   │       │   ├── getLoginPage.ts
│   │       │   ├── getLoginPageBranding.ts
│   │       │   ├── index.ts
│   │       │   ├── loadLoginPage.ts
│   │       │   ├── loadLoginPageBranding.ts
│   │       │   ├── updateLoginPage.ts
│   │       │   └── upsertLoginPageBranding.ts
│   │       ├── misc/
│   │       │   ├── index.ts
│   │       │   └── sendSupportEmail.ts
│   │       ├── org/
│   │       │   ├── index.ts
│   │       │   └── sendUsageNotifications.ts
│   │       ├── orgIdp/
│   │       │   ├── createOrgOidcIdp.ts
│   │       │   ├── deleteOrgIdp.ts
│   │       │   ├── getOrgIdp.ts
│   │       │   ├── index.ts
│   │       │   ├── listOrgIdps.ts
│   │       │   └── updateOrgOidcIdp.ts
│   │       ├── re-key/
│   │       │   ├── index.ts
│   │       │   ├── reGenerateClientSecret.ts
│   │       │   ├── reGenerateExitNodeSecret.ts
│   │       │   └── reGenerateSiteSecret.ts
│   │       ├── remoteExitNode/
│   │       │   ├── createRemoteExitNode.ts
│   │       │   ├── deleteRemoteExitNode.ts
│   │       │   ├── getRemoteExitNode.ts
│   │       │   ├── getRemoteExitNodeToken.ts
│   │       │   ├── handleRemoteExitNodePingMessage.ts
│   │       │   ├── handleRemoteExitNodeRegisterMessage.ts
│   │       │   ├── index.ts
│   │       │   ├── listRemoteExitNodes.ts
│   │       │   ├── pickRemoteExitNodeDefaults.ts
│   │       │   └── quickStartRemoteExitNode.ts
│   │       ├── resource/
│   │       │   ├── getMaintenanceInfo.ts
│   │       │   └── index.ts
│   │       ├── ssh/
│   │       │   ├── index.ts
│   │       │   └── signSshKey.ts
│   │       └── ws/
│   │           ├── index.ts
│   │           ├── messageHandlers.ts
│   │           └── ws.ts
│   ├── routers/
│   │   ├── accessToken/
│   │   │   ├── deleteAccessToken.ts
│   │   │   ├── generateAccessToken.ts
│   │   │   ├── index.ts
│   │   │   └── listAccessTokens.ts
│   │   ├── apiKeys/
│   │   │   ├── createOrgApiKey.ts
│   │   │   ├── createRootApiKey.ts
│   │   │   ├── deleteApiKey.ts
│   │   │   ├── deleteOrgApiKey.ts
│   │   │   ├── getApiKey.ts
│   │   │   ├── index.ts
│   │   │   ├── listApiKeyActions.ts
│   │   │   ├── listOrgApiKeys.ts
│   │   │   ├── listRootApiKeys.ts
│   │   │   ├── setApiKeyActions.ts
│   │   │   └── setApiKeyOrgs.ts
│   │   ├── auditLogs/
│   │   │   ├── exportRequestAuditLog.ts
│   │   │   ├── generateCSV.ts
│   │   │   ├── index.ts
│   │   │   ├── queryRequestAnalytics.ts
│   │   │   ├── queryRequestAuditLog.ts
│   │   │   └── types.ts
│   │   ├── auth/
│   │   │   ├── changePassword.ts
│   │   │   ├── checkResourceSession.ts
│   │   │   ├── deleteMyAccount.ts
│   │   │   ├── disable2fa.ts
│   │   │   ├── index.ts
│   │   │   ├── initialSetupComplete.ts
│   │   │   ├── login.ts
│   │   │   ├── logout.ts
│   │   │   ├── lookupUser.ts
│   │   │   ├── pollDeviceWebAuth.ts
│   │   │   ├── requestEmailVerificationCode.ts
│   │   │   ├── requestPasswordReset.ts
│   │   │   ├── requestTotpSecret.ts
│   │   │   ├── resetPassword.ts
│   │   │   ├── securityKey.ts
│   │   │   ├── setServerAdmin.ts
│   │   │   ├── signup.ts
│   │   │   ├── startDeviceWebAuth.ts
│   │   │   ├── types.ts
│   │   │   ├── validateSetupToken.ts
│   │   │   ├── verifyDeviceWebAuth.ts
│   │   │   ├── verifyEmail.ts
│   │   │   └── verifyTotp.ts
│   │   ├── badger/
│   │   │   ├── exchangeSession.ts
│   │   │   ├── index.ts
│   │   │   ├── logRequestAudit.ts
│   │   │   ├── verifySession.test.ts
│   │   │   └── verifySession.ts
│   │   ├── billing/
│   │   │   ├── types.ts
│   │   │   └── webhooks.ts
│   │   ├── blueprints/
│   │   │   ├── applyJSONBlueprint.ts
│   │   │   ├── applyYAMLBlueprint.ts
│   │   │   ├── getBlueprint.ts
│   │   │   ├── index.ts
│   │   │   ├── listBlueprints.ts
│   │   │   └── types.ts
│   │   ├── certificates/
│   │   │   ├── createCertificate.ts
│   │   │   └── types.ts
│   │   ├── client/
│   │   │   ├── archiveClient.ts
│   │   │   ├── blockClient.ts
│   │   │   ├── createClient.ts
│   │   │   ├── createUserClient.ts
│   │   │   ├── deleteClient.ts
│   │   │   ├── getClient.ts
│   │   │   ├── index.ts
│   │   │   ├── listClients.ts
│   │   │   ├── listUserDevices.ts
│   │   │   ├── pickClientDefaults.ts
│   │   │   ├── targets.ts
│   │   │   ├── terminate.ts
│   │   │   ├── unarchiveClient.ts
│   │   │   ├── unblockClient.ts
│   │   │   └── updateClient.ts
│   │   ├── domain/
│   │   │   ├── createOrgDomain.ts
│   │   │   ├── deleteOrgDomain.ts
│   │   │   ├── getDNSRecords.ts
│   │   │   ├── getDomain.ts
│   │   │   ├── index.ts
│   │   │   ├── listDomains.ts
│   │   │   ├── restartOrgDomain.ts
│   │   │   ├── types.ts
│   │   │   └── updateDomain.ts
│   │   ├── external.ts
│   │   ├── generatedLicense/
│   │   │   └── types.ts
│   │   ├── gerbil/
│   │   │   ├── createExitNode.ts
│   │   │   ├── getAllRelays.ts
│   │   │   ├── getConfig.ts
│   │   │   ├── getResolvedHostname.ts
│   │   │   ├── index.ts
│   │   │   ├── peers.ts
│   │   │   ├── receiveBandwidth.ts
│   │   │   └── updateHolePunch.ts
│   │   ├── hybrid.ts
│   │   ├── idp/
│   │   │   ├── createIdpOrgPolicy.ts
│   │   │   ├── createOidcIdp.ts
│   │   │   ├── deleteIdp.ts
│   │   │   ├── deleteIdpOrgPolicy.ts
│   │   │   ├── generateOidcUrl.ts
│   │   │   ├── getIdp.ts
│   │   │   ├── index.ts
│   │   │   ├── listIdpOrgPolicies.ts
│   │   │   ├── listIdps.ts
│   │   │   ├── updateIdpOrgPolicy.ts
│   │   │   ├── updateOidcIdp.ts
│   │   │   └── validateOidcCallback.ts
│   │   ├── integration.ts
│   │   ├── internal.ts
│   │   ├── license/
│   │   │   └── types.ts
│   │   ├── loginPage/
│   │   │   └── types.ts
│   │   ├── newt/
│   │   │   ├── buildConfiguration.ts
│   │   │   ├── createNewt.ts
│   │   │   ├── dockerSocket.ts
│   │   │   ├── getNewtToken.ts
│   │   │   ├── handleApplyBlueprintMessage.ts
│   │   │   ├── handleGetConfigMessage.ts
│   │   │   ├── handleNewtDisconnectingMessage.ts
│   │   │   ├── handleNewtPingMessage.ts
│   │   │   ├── handleNewtPingRequestMessage.ts
│   │   │   ├── handleNewtRegisterMessage.ts
│   │   │   ├── handleReceiveBandwidthMessage.ts
│   │   │   ├── handleSocketMessages.ts
│   │   │   ├── index.ts
│   │   │   ├── peers.ts
│   │   │   ├── sync.ts
│   │   │   └── targets.ts
│   │   ├── olm/
│   │   │   ├── archiveUserOlm.ts
│   │   │   ├── buildConfiguration.ts
│   │   │   ├── createOlm.ts
│   │   │   ├── createUserOlm.ts
│   │   │   ├── deleteUserOlm.ts
│   │   │   ├── error.ts
│   │   │   ├── fingerprintingUtils.ts
│   │   │   ├── getOlmToken.ts
│   │   │   ├── getUserOlm.ts
│   │   │   ├── handleOlmDisconnectingMessage.ts
│   │   │   ├── handleOlmPingMessage.ts
│   │   │   ├── handleOlmRegisterMessage.ts
│   │   │   ├── handleOlmRelayMessage.ts
│   │   │   ├── handleOlmServerInitAddPeerHandshake.ts
│   │   │   ├── handleOlmServerPeerAddMessage.ts
│   │   │   ├── handleOlmUnRelayMessage.ts
│   │   │   ├── index.ts
│   │   │   ├── listUserOlms.ts
│   │   │   ├── peers.ts
│   │   │   ├── recoverOlmWithFingerprint.ts
│   │   │   ├── sync.ts
│   │   │   └── unarchiveUserOlm.ts
│   │   ├── org/
│   │   │   ├── checkId.ts
│   │   │   ├── checkOrgUserAccess.ts
│   │   │   ├── createOrg.ts
│   │   │   ├── deleteOrg.ts
│   │   │   ├── getOrg.ts
│   │   │   ├── getOrgOverview.ts
│   │   │   ├── index.ts
│   │   │   ├── listOrgs.ts
│   │   │   ├── listUserOrgs.ts
│   │   │   ├── pickOrgDefaults.ts
│   │   │   ├── resetOrgBandwidth.ts
│   │   │   └── updateOrg.ts
│   │   ├── orgIdp/
│   │   │   └── types.ts
│   │   ├── remoteExitNode/
│   │   │   └── types.ts
│   │   ├── resource/
│   │   │   ├── addEmailToResourceWhitelist.ts
│   │   │   ├── addRoleToResource.ts
│   │   │   ├── addUserToResource.ts
│   │   │   ├── authWithAccessToken.ts
│   │   │   ├── authWithPassword.ts
│   │   │   ├── authWithPincode.ts
│   │   │   ├── authWithWhitelist.ts
│   │   │   ├── createResource.ts
│   │   │   ├── createResourceRule.ts
│   │   │   ├── deleteResource.ts
│   │   │   ├── deleteResourceRule.ts
│   │   │   ├── getExchangeToken.ts
│   │   │   ├── getResource.ts
│   │   │   ├── getResourceAuthInfo.ts
│   │   │   ├── getResourceWhitelist.ts
│   │   │   ├── getUserResources.ts
│   │   │   ├── index.ts
│   │   │   ├── listAllResourceNames.ts
│   │   │   ├── listResourceRoles.ts
│   │   │   ├── listResourceRules.ts
│   │   │   ├── listResourceUsers.ts
│   │   │   ├── listResources.ts
│   │   │   ├── removeEmailFromResourceWhitelist.ts
│   │   │   ├── removeRoleFromResource.ts
│   │   │   ├── removeUserFromResource.ts
│   │   │   ├── setResourceHeaderAuth.ts
│   │   │   ├── setResourcePassword.ts
│   │   │   ├── setResourcePincode.ts
│   │   │   ├── setResourceRoles.ts
│   │   │   ├── setResourceUsers.ts
│   │   │   ├── setResourceWhitelist.ts
│   │   │   ├── types.ts
│   │   │   ├── updateResource.ts
│   │   │   └── updateResourceRule.ts
│   │   ├── role/
│   │   │   ├── addRoleAction.ts
│   │   │   ├── addRoleSite.ts
│   │   │   ├── createRole.ts
│   │   │   ├── deleteRole.ts
│   │   │   ├── getRole.ts
│   │   │   ├── index.ts
│   │   │   ├── listRoleActions.ts
│   │   │   ├── listRoleResources.ts
│   │   │   ├── listRoleSites.ts
│   │   │   ├── listRoles.ts
│   │   │   ├── removeRoleAction.ts
│   │   │   ├── removeRoleResource.ts
│   │   │   ├── removeRoleSite.ts
│   │   │   └── updateRole.ts
│   │   ├── serverInfo/
│   │   │   ├── getServerInfo.ts
│   │   │   └── index.ts
│   │   ├── site/
│   │   │   ├── createSite.ts
│   │   │   ├── deleteSite.ts
│   │   │   ├── getSite.ts
│   │   │   ├── index.ts
│   │   │   ├── listSiteRoles.ts
│   │   │   ├── listSites.ts
│   │   │   ├── pickSiteDefaults.ts
│   │   │   ├── socketIntegration.ts
│   │   │   └── updateSite.ts
│   │   ├── siteResource/
│   │   │   ├── addClientToSiteResource.ts
│   │   │   ├── addRoleToSiteResource.ts
│   │   │   ├── addUserToSiteResource.ts
│   │   │   ├── batchAddClientToSiteResources.ts
│   │   │   ├── createSiteResource.ts
│   │   │   ├── deleteSiteResource.ts
│   │   │   ├── getSiteResource.ts
│   │   │   ├── index.ts
│   │   │   ├── listAllSiteResourcesByOrg.ts
│   │   │   ├── listSiteResourceClients.ts
│   │   │   ├── listSiteResourceRoles.ts
│   │   │   ├── listSiteResourceUsers.ts
│   │   │   ├── listSiteResources.ts
│   │   │   ├── removeClientFromSiteResource.ts
│   │   │   ├── removeRoleFromSiteResource.ts
│   │   │   ├── removeUserFromSiteResource.ts
│   │   │   ├── setSiteResourceClients.ts
│   │   │   ├── setSiteResourceRoles.ts
│   │   │   ├── setSiteResourceUsers.ts
│   │   │   └── updateSiteResource.ts
│   │   ├── supporterKey/
│   │   │   ├── hideSupporterKey.ts
│   │   │   ├── index.ts
│   │   │   ├── isSupporterKeyVisible.ts
│   │   │   └── validateSupporterKey.ts
│   │   ├── target/
│   │   │   ├── createTarget.ts
│   │   │   ├── deleteTarget.ts
│   │   │   ├── getTarget.ts
│   │   │   ├── handleHealthcheckStatusMessage.ts
│   │   │   ├── helpers.ts
│   │   │   ├── index.ts
│   │   │   ├── listTargets.ts
│   │   │   └── updateTarget.ts
│   │   ├── traefik/
│   │   │   ├── configSchema.ts
│   │   │   ├── index.ts
│   │   │   └── traefikConfigProvider.ts
│   │   ├── user/
│   │   │   ├── acceptInvite.ts
│   │   │   ├── addUserAction.ts
│   │   │   ├── addUserRole.ts
│   │   │   ├── addUserSite.ts
│   │   │   ├── adminGeneratePasswordResetCode.ts
│   │   │   ├── adminGetUser.ts
│   │   │   ├── adminListUsers.ts
│   │   │   ├── adminRemoveUser.ts
│   │   │   ├── adminUpdateUser2FA.ts
│   │   │   ├── createOrgUser.ts
│   │   │   ├── getOrgUser.ts
│   │   │   ├── getOrgUserByUsername.ts
│   │   │   ├── getUser.ts
│   │   │   ├── index.ts
│   │   │   ├── inviteUser.ts
│   │   │   ├── listInvitations.ts
│   │   │   ├── listUsers.ts
│   │   │   ├── myDevice.ts
│   │   │   ├── removeInvitation.ts
│   │   │   ├── removeUserAction.ts
│   │   │   ├── removeUserOrg.ts
│   │   │   ├── removeUserResource.ts
│   │   │   ├── removeUserSite.ts
│   │   │   └── updateOrgUser.ts
│   │   └── ws/
│   │       ├── checkRoundTripMessage.ts
│   │       ├── handleRoundTripMessage.ts
│   │       ├── index.ts
│   │       ├── messageHandlers.ts
│   │       ├── types.ts
│   │       └── ws.ts
│   ├── setup/
│   │   ├── .gitignore
│   │   ├── clearStaleData.ts
│   │   ├── copyInConfig.ts
│   │   ├── ensureActions.ts
│   │   ├── ensureSetupToken.ts
│   │   ├── index.ts
│   │   ├── migrationsPg.ts
│   │   ├── migrationsSqlite.ts
│   │   ├── scriptsPg/
│   │   │   ├── 1.10.0.ts
│   │   │   ├── 1.10.2.ts
│   │   │   ├── 1.11.0.ts
│   │   │   ├── 1.11.1.ts
│   │   │   ├── 1.12.0.ts
│   │   │   ├── 1.13.0.ts
│   │   │   ├── 1.14.0.ts
│   │   │   ├── 1.15.0.ts
│   │   │   ├── 1.15.3.ts
│   │   │   ├── 1.15.4.ts
│   │   │   ├── 1.16.0.ts
│   │   │   ├── 1.6.0.ts
│   │   │   ├── 1.7.0.ts
│   │   │   ├── 1.8.0.ts
│   │   │   └── 1.9.0.ts
│   │   └── scriptsSqlite/
│   │       ├── 1.0.0-beta1.ts
│   │       ├── 1.0.0-beta10.ts
│   │       ├── 1.0.0-beta12.ts
│   │       ├── 1.0.0-beta13.ts
│   │       ├── 1.0.0-beta15.ts
│   │       ├── 1.0.0-beta2.ts
│   │       ├── 1.0.0-beta3.ts
│   │       ├── 1.0.0-beta5.ts
│   │       ├── 1.0.0-beta6.ts
│   │       ├── 1.0.0-beta9.ts
│   │       ├── 1.0.0.ts
│   │       ├── 1.1.0.ts
│   │       ├── 1.10.0.ts
│   │       ├── 1.10.1.ts
│   │       ├── 1.10.2.ts
│   │       ├── 1.11.0.ts
│   │       ├── 1.11.1.ts
│   │       ├── 1.12.0.ts
│   │       ├── 1.13.0.ts
│   │       ├── 1.14.0.ts
│   │       ├── 1.15.0.ts
│   │       ├── 1.15.3.ts
│   │       ├── 1.15.4.ts
│   │       ├── 1.16.0.ts
│   │       ├── 1.2.0.ts
│   │       ├── 1.3.0.ts
│   │       ├── 1.5.0.ts
│   │       ├── 1.6.0.ts
│   │       ├── 1.7.0.ts
│   │       ├── 1.8.0.ts
│   │       └── 1.9.0.ts
│   └── types/
│       ├── ArrayElement.ts
│       ├── Auth.ts
│       ├── ErrorResponse.ts
│       ├── HttpCode.ts
│       ├── MessageResponse.ts
│       ├── Pagination.ts
│       ├── Response.ts
│       ├── Tiers.ts
│       └── UserTypes.ts
├── src/
│   ├── actions/
│   │   └── server.ts
│   ├── app/
│   │   ├── [orgId]/
│   │   │   ├── layout.tsx
│   │   │   ├── page.tsx
│   │   │   └── settings/
│   │   │       ├── (private)/
│   │   │       │   ├── access/
│   │   │       │   │   └── approvals/
│   │   │       │   │       └── page.tsx
│   │   │       │   ├── billing/
│   │   │       │   │   ├── layout.tsx
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── idp/
│   │   │       │   │   ├── [idpId]/
│   │   │       │   │   │   ├── general/
│   │   │       │   │   │   │   └── page.tsx
│   │   │       │   │   │   ├── layout.tsx
│   │   │       │   │   │   └── page.tsx
│   │   │       │   │   ├── create/
│   │   │       │   │   │   └── page.tsx
│   │   │       │   │   ├── layout.tsx
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── license/
│   │   │       │   │   ├── layout.tsx
│   │   │       │   │   └── page.tsx
│   │   │       │   └── remote-exit-nodes/
│   │   │       │       ├── [remoteExitNodeId]/
│   │   │       │       │   ├── credentials/
│   │   │       │       │   │   └── page.tsx
│   │   │       │       │   ├── layout.tsx
│   │   │       │       │   └── page.tsx
│   │   │       │       ├── create/
│   │   │       │       │   └── page.tsx
│   │   │       │       └── page.tsx
│   │   │       ├── access/
│   │   │       │   ├── invitations/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── layout.tsx
│   │   │       │   ├── page.tsx
│   │   │       │   ├── roles/
│   │   │       │   │   └── page.tsx
│   │   │       │   └── users/
│   │   │       │       ├── [userId]/
│   │   │       │       │   ├── access-controls/
│   │   │       │       │   │   └── page.tsx
│   │   │       │       │   ├── layout.tsx
│   │   │       │       │   └── page.tsx
│   │   │       │       ├── create/
│   │   │       │       │   └── page.tsx
│   │   │       │       └── page.tsx
│   │   │       ├── api-keys/
│   │   │       │   ├── [apiKeyId]/
│   │   │       │   │   ├── layout.tsx
│   │   │       │   │   ├── page.tsx
│   │   │       │   │   └── permissions/
│   │   │       │   │       └── page.tsx
│   │   │       │   ├── create/
│   │   │       │   │   └── page.tsx
│   │   │       │   └── page.tsx
│   │   │       ├── blueprints/
│   │   │       │   ├── [blueprintId]/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── create/
│   │   │       │   │   └── page.tsx
│   │   │       │   └── page.tsx
│   │   │       ├── clients/
│   │   │       │   ├── layout.tsx
│   │   │       │   ├── machine/
│   │   │       │   │   ├── [niceId]/
│   │   │       │   │   │   ├── credentials/
│   │   │       │   │   │   │   └── page.tsx
│   │   │       │   │   │   ├── general/
│   │   │       │   │   │   │   └── page.tsx
│   │   │       │   │   │   ├── layout.tsx
│   │   │       │   │   │   └── page.tsx
│   │   │       │   │   ├── create/
│   │   │       │   │   │   └── page.tsx
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── page.tsx
│   │   │       │   └── user/
│   │   │       │       ├── [niceId]/
│   │   │       │       │   ├── general/
│   │   │       │       │   │   └── page.tsx
│   │   │       │       │   ├── layout.tsx
│   │   │       │       │   └── page.tsx
│   │   │       │       └── page.tsx
│   │   │       ├── domains/
│   │   │       │   ├── [domainId]/
│   │   │       │   │   └── page.tsx
│   │   │       │   └── page.tsx
│   │   │       ├── general/
│   │   │       │   ├── auth-page/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── layout.tsx
│   │   │       │   ├── page.tsx
│   │   │       │   └── security/
│   │   │       │       └── page.tsx
│   │   │       ├── layout.tsx
│   │   │       ├── logs/
│   │   │       │   ├── access/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── action/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── analytics/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── layout.tsx
│   │   │       │   ├── page.tsx
│   │   │       │   └── request/
│   │   │       │       └── page.tsx
│   │   │       ├── not-found.tsx
│   │   │       ├── page.tsx
│   │   │       ├── resources/
│   │   │       │   ├── client/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── page.tsx
│   │   │       │   └── proxy/
│   │   │       │       ├── [niceId]/
│   │   │       │       │   ├── authentication/
│   │   │       │       │   │   └── page.tsx
│   │   │       │       │   ├── general/
│   │   │       │       │   │   └── page.tsx
│   │   │       │       │   ├── layout.tsx
│   │   │       │       │   ├── page.tsx
│   │   │       │       │   ├── proxy/
│   │   │       │       │   │   └── page.tsx
│   │   │       │       │   └── rules/
│   │   │       │       │       └── page.tsx
│   │   │       │       ├── create/
│   │   │       │       │   └── page.tsx
│   │   │       │       └── page.tsx
│   │   │       ├── share-links/
│   │   │       │   └── page.tsx
│   │   │       └── sites/
│   │   │           ├── [niceId]/
│   │   │           │   ├── credentials/
│   │   │           │   │   └── page.tsx
│   │   │           │   ├── general/
│   │   │           │   │   └── page.tsx
│   │   │           │   ├── layout.tsx
│   │   │           │   ├── page.tsx
│   │   │           │   └── wireguardConfig.ts
│   │   │           ├── create/
│   │   │           │   └── page.tsx
│   │   │           └── page.tsx
│   │   ├── admin/
│   │   │   ├── api-keys/
│   │   │   │   ├── [apiKeyId]/
│   │   │   │   │   ├── layout.tsx
│   │   │   │   │   ├── page.tsx
│   │   │   │   │   └── permissions/
│   │   │   │   │       └── page.tsx
│   │   │   │   ├── create/
│   │   │   │   │   └── page.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── idp/
│   │   │   │   ├── [idpId]/
│   │   │   │   │   ├── general/
│   │   │   │   │   │   └── page.tsx
│   │   │   │   │   ├── layout.tsx
│   │   │   │   │   ├── page.tsx
│   │   │   │   │   └── policies/
│   │   │   │   │       └── page.tsx
│   │   │   │   ├── create/
│   │   │   │   │   └── page.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── layout.tsx
│   │   │   ├── license/
│   │   │   │   ├── layout.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── page.tsx
│   │   │   └── users/
│   │   │       ├── AdminUsersTable.tsx
│   │   │       ├── [userId]/
│   │   │       │   ├── general/
│   │   │       │   │   └── page.tsx
│   │   │       │   ├── layout.tsx
│   │   │       │   └── page.tsx
│   │   │       └── page.tsx
│   │   ├── auth/
│   │   │   ├── 2fa/
│   │   │   │   └── setup/
│   │   │   │       └── page.tsx
│   │   │   ├── delete-account/
│   │   │   │   ├── DeleteAccountClient.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── idp/
│   │   │   │   └── [idpId]/
│   │   │   │       └── oidc/
│   │   │   │           └── callback/
│   │   │   │               └── page.tsx
│   │   │   ├── initial-setup/
│   │   │   │   ├── layout.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── layout.tsx
│   │   │   ├── login/
│   │   │   │   ├── device/
│   │   │   │   │   ├── page.tsx
│   │   │   │   │   └── success/
│   │   │   │   │       └── page.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── org/
│   │   │   │   ├── [orgId]/
│   │   │   │   │   └── page.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── reset-password/
│   │   │   │   ├── ResetPasswordForm.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── resource/
│   │   │   │   └── [resourceGuid]/
│   │   │   │       └── page.tsx
│   │   │   ├── signup/
│   │   │   │   └── page.tsx
│   │   │   └── verify-email/
│   │   │       └── page.tsx
│   │   ├── globals.css
│   │   ├── invite/
│   │   │   └── page.tsx
│   │   ├── layout.tsx
│   │   ├── maintenance-screen/
│   │   │   └── page.tsx
│   │   ├── navigation.tsx
│   │   ├── not-found.tsx
│   │   ├── page.tsx
│   │   ├── robots.ts
│   │   ├── s/
│   │   │   └── [accessToken]/
│   │   │       └── page.tsx
│   │   └── setup/
│   │       ├── layout.tsx
│   │       └── page.tsx
│   ├── components/
│   │   ├── AccessPageHeaderAndNav.tsx
│   │   ├── AccessToken.tsx
│   │   ├── AccessTokenUsage.tsx
│   │   ├── ActionBanner.tsx
│   │   ├── AdminIdpDataTable.tsx
│   │   ├── AdminIdpTable.tsx
│   │   ├── AdminUsersDataTable.tsx
│   │   ├── AdminUsersTable.tsx
│   │   ├── ApiKeysDataTable.tsx
│   │   ├── ApiKeysTable.tsx
│   │   ├── ApplyInternalRedirect.tsx
│   │   ├── ApprovalFeed.tsx
│   │   ├── ApprovalsBanner.tsx
│   │   ├── ApprovalsEmptyState.tsx
│   │   ├── AuthPageBrandingForm.tsx
│   │   ├── AuthPageSettings.tsx
│   │   ├── AutoLoginHandler.tsx
│   │   ├── AutoProvisionConfigWidget.tsx
│   │   ├── BlueprintDetailsForm.tsx
│   │   ├── BlueprintsTable.tsx
│   │   ├── BrandingLogo.tsx
│   │   ├── CertificateStatus.tsx
│   │   ├── ChangePasswordDialog.tsx
│   │   ├── ChangePasswordForm.tsx
│   │   ├── ClientDownloadBanner.tsx
│   │   ├── ClientInfoCard.tsx
│   │   ├── ClientResourcesTable.tsx
│   │   ├── ClientsDataTable.tsx
│   │   ├── ColumnFilter.tsx
│   │   ├── ColumnFilterButton.tsx
│   │   ├── ConfirmDeleteDialog.tsx
│   │   ├── ContainersSelector.tsx
│   │   ├── CopyTextBox.tsx
│   │   ├── CopyToClipboard.tsx
│   │   ├── CreateBlueprintForm.tsx
│   │   ├── CreateDomainForm.tsx
│   │   ├── CreateInternalResourceDialog.tsx
│   │   ├── CreateRoleForm.tsx
│   │   ├── CreateShareLinkForm.tsx
│   │   ├── Credenza.tsx
│   │   ├── CustomDomainInput.tsx
│   │   ├── DNSRecordTable.tsx
│   │   ├── DNSRecordsDataTable.tsx
│   │   ├── DashboardLoginForm.tsx
│   │   ├── DataTablePagination.tsx
│   │   ├── DateTimePicker.tsx
│   │   ├── DeleteAccountConfirmDialog.tsx
│   │   ├── DeleteRoleForm.tsx
│   │   ├── DeviceAuthConfirmation.tsx
│   │   ├── DeviceLoginForm.tsx
│   │   ├── Disable2FaForm.tsx
│   │   ├── DismissableBanner.tsx
│   │   ├── DomainCertForm.tsx
│   │   ├── DomainInfoCard.tsx
│   │   ├── DomainPicker.tsx
│   │   ├── DomainsDataTable.tsx
│   │   ├── DomainsTable.tsx
│   │   ├── EditInternalResourceDialog.tsx
│   │   ├── EditRoleForm.tsx
│   │   ├── Enable2FaDialog.tsx
│   │   ├── Enable2FaForm.tsx
│   │   ├── ExitNodeInfoCard.tsx
│   │   ├── ExitNodesDataTable.tsx
│   │   ├── ExitNodesTable.tsx
│   │   ├── GenerateLicenseKeyForm.tsx
│   │   ├── GenerateLicenseKeysTable.tsx
│   │   ├── HeadersInput.tsx
│   │   ├── HealthCheckDialog.tsx
│   │   ├── HorizontalTabs.tsx
│   │   ├── IdpCreateWizard.tsx
│   │   ├── IdpGlobalModeBanner.tsx
│   │   ├── IdpLoginButtons.tsx
│   │   ├── IdpTypeBadge.tsx
│   │   ├── InfoSection.tsx
│   │   ├── InternalResourceForm.tsx
│   │   ├── InvitationsDataTable.tsx
│   │   ├── InvitationsTable.tsx
│   │   ├── InviteStatusCard.tsx
│   │   ├── Layout.tsx
│   │   ├── LayoutHeader.tsx
│   │   ├── LayoutMobileMenu.tsx
│   │   ├── LayoutSidebar.tsx
│   │   ├── LicenseKeysDataTable.tsx
│   │   ├── LicenseViolation.tsx
│   │   ├── LocaleSwitcher.tsx
│   │   ├── LocaleSwitcherSelect.tsx
│   │   ├── LogAnalyticsData.tsx
│   │   ├── LogDataTable.tsx
│   │   ├── LoginCardHeader.tsx
│   │   ├── LoginForm.tsx
│   │   ├── LoginOrgSelector.tsx
│   │   ├── LoginPasswordForm.tsx
│   │   ├── MachineClientsBanner.tsx
│   │   ├── MachineClientsTable.tsx
│   │   ├── MemberResourcesPortal.tsx
│   │   ├── MfaInputForm.tsx
│   │   ├── NewPricingLicenseForm.tsx
│   │   ├── OptionSelect.tsx
│   │   ├── OrgApiKeysDataTable.tsx
│   │   ├── OrgApiKeysTable.tsx
│   │   ├── OrgIdpDataTable.tsx
│   │   ├── OrgIdpTable.tsx
│   │   ├── OrgInfoCard.tsx
│   │   ├── OrgLoginPage.tsx
│   │   ├── OrgPolicyRequired.tsx
│   │   ├── OrgPolicyResult.tsx
│   │   ├── OrgSelectionForm.tsx
│   │   ├── OrgSelector.tsx
│   │   ├── OrgSignInLink.tsx
│   │   ├── OrganizationLanding.tsx
│   │   ├── OrganizationLandingCard.tsx
│   │   ├── PaidFeaturesAlert.tsx
│   │   ├── PathMatchRenameModal.tsx
│   │   ├── PermissionsSelectBox.tsx
│   │   ├── PlaceHolderLoader.tsx
│   │   ├── PolicyDataTable.tsx
│   │   ├── PolicyTable.tsx
│   │   ├── PrivateResourcesBanner.tsx
│   │   ├── ProductUpdates.tsx
│   │   ├── ProfessionalContentOverlay.tsx
│   │   ├── ProfileIcon.tsx
│   │   ├── ProxyResourcesBanner.tsx
│   │   ├── ProxyResourcesTable.tsx
│   │   ├── QRContainer.tsx
│   │   ├── RedirectToOrg.tsx
│   │   ├── RefreshButton.tsx
│   │   ├── RegenerateInvitationForm.tsx
│   │   ├── RegionSelector.tsx
│   │   ├── ResetPasswordForm.tsx
│   │   ├── ResourceAccessDenied.tsx
│   │   ├── ResourceAuthPortal.tsx
│   │   ├── ResourceInfoBox.tsx
│   │   ├── ResourceNotFound.tsx
│   │   ├── RestartDomainButton.tsx
│   │   ├── RoleForm.tsx
│   │   ├── RolesDataTable.tsx
│   │   ├── RolesTable.tsx
│   │   ├── SecurityKeyAuthButton.tsx
│   │   ├── SecurityKeyForm.tsx
│   │   ├── SetLastOrgCookie.tsx
│   │   ├── SetResourceHeaderAuthForm.tsx
│   │   ├── SetResourcePasswordForm.tsx
│   │   ├── SetResourcePincodeForm.tsx
│   │   ├── Settings.tsx
│   │   ├── SettingsSectionTitle.tsx
│   │   ├── ShareLinksDataTable.tsx
│   │   ├── ShareLinksSplash.tsx
│   │   ├── ShareLinksTable.tsx
│   │   ├── SidebarLicenseButton.tsx
│   │   ├── SidebarNav.tsx
│   │   ├── SidebarSupportButton.tsx
│   │   ├── SignupForm.tsx
│   │   ├── SiteInfoCard.tsx
│   │   ├── SitePriceCalculator.tsx
│   │   ├── SitesBanner.tsx
│   │   ├── SitesSplashCard.tsx
│   │   ├── SitesTable.tsx
│   │   ├── SmartLoginForm.tsx
│   │   ├── SplashImage.tsx
│   │   ├── StoreInternalRedirect.tsx
│   │   ├── StrategySelect.tsx
│   │   ├── SubscriptionViolation.tsx
│   │   ├── SupporterMessage.tsx
│   │   ├── SupporterStatus.tsx
│   │   ├── SwitchInput.tsx
│   │   ├── TailwindIndicator.tsx
│   │   ├── TanstackQueryProvider.tsx
│   │   ├── ThemeSwitcher.tsx
│   │   ├── TopbarNav.tsx
│   │   ├── Toploader.tsx
│   │   ├── TwoFactorSetupForm.tsx
│   │   ├── UserDevicesTable.tsx
│   │   ├── UserProfileCard.tsx
│   │   ├── UsersDataTable.tsx
│   │   ├── UsersTable.tsx
│   │   ├── ValidateOidcToken.tsx
│   │   ├── ValidateSessionTransferToken.tsx
│   │   ├── VerifyEmailForm.tsx
│   │   ├── ViewDevicesDialog.tsx
│   │   ├── ViewportHeightFix.tsx
│   │   ├── WorldMap.tsx
│   │   ├── newt-install-commands.tsx
│   │   ├── olm-install-commands.tsx
│   │   ├── resource-target-address-item.tsx
│   │   ├── tags/
│   │   │   ├── autocomplete.tsx
│   │   │   ├── tag-input.tsx
│   │   │   ├── tag-list.tsx
│   │   │   ├── tag-popover.tsx
│   │   │   └── tag.tsx
│   │   └── ui/
│   │       ├── alert.tsx
│   │       ├── avatar.tsx
│   │       ├── badge.tsx
│   │       ├── breadcrumb.tsx
│   │       ├── button.tsx
│   │       ├── calendar.tsx
│   │       ├── card.tsx
│   │       ├── chart.tsx
│   │       ├── checkbox.tsx
│   │       ├── collapsible.tsx
│   │       ├── command.tsx
│   │       ├── controlled-data-table.tsx
│   │       ├── data-table.tsx
│   │       ├── dialog.tsx
│   │       ├── drawer.tsx
│   │       ├── dropdown-menu.tsx
│   │       ├── form.tsx
│   │       ├── info-popup.tsx
│   │       ├── input-otp.tsx
│   │       ├── input.tsx
│   │       ├── label.tsx
│   │       ├── popover.tsx
│   │       ├── progress.tsx
│   │       ├── radio-group.tsx
│   │       ├── scroll-area.tsx
│   │       ├── select.tsx
│   │       ├── separator.tsx
│   │       ├── sheet.tsx
│   │       ├── switch.tsx
│   │       ├── table.tsx
│   │       ├── tabs.tsx
│   │       ├── textarea.tsx
│   │       ├── toast.tsx
│   │       ├── toaster.tsx
│   │       └── tooltip.tsx
│   ├── contexts/
│   │   ├── apiKeyContext.ts
│   │   ├── clientContext.ts
│   │   ├── domainContext.ts
│   │   ├── envContext.ts
│   │   ├── licenseStatusContext.ts
│   │   ├── orgContext.ts
│   │   ├── orgUserContext.ts
│   │   ├── remoteExitNodeContext.ts
│   │   ├── resourceContext.ts
│   │   ├── siteContext.ts
│   │   ├── subscriptionStatusContext.ts
│   │   ├── supporterStatusContext.ts
│   │   └── userContext.ts
│   ├── hooks/
│   │   ├── useApikeyContext.ts
│   │   ├── useCertificate.ts
│   │   ├── useClientContext.ts
│   │   ├── useDomainContext.ts
│   │   ├── useEnvContext.ts
│   │   ├── useLicenseStatusContext.ts
│   │   ├── useLocalStorage.ts
│   │   ├── useMediaQuery.ts
│   │   ├── useNavigationContext.ts
│   │   ├── useOrgContext.ts
│   │   ├── useOrgUserContext.ts
│   │   ├── usePaidStatus.ts
│   │   ├── useRemoteExitNodeContext.ts
│   │   ├── useResourceContext.ts
│   │   ├── useSiteContext.ts
│   │   ├── useStoredColumnVisibility.ts
│   │   ├── useStoredPageSize.ts
│   │   ├── useSubscriptionStatusContext.ts
│   │   ├── useSupporterStatusContext.ts
│   │   ├── useToast.ts
│   │   ├── useUserContext.ts
│   │   └── useUserLookup.ts
│   ├── i18n/
│   │   ├── config.ts
│   │   └── request.ts
│   ├── lib/
│   │   ├── api/
│   │   │   ├── cookies.ts
│   │   │   ├── formatAxiosError.ts
│   │   │   ├── getCachedOrg.ts
│   │   │   ├── getCachedOrgUser.ts
│   │   │   ├── getCachedSubscription.ts
│   │   │   ├── index.ts
│   │   │   └── isOrgSubscribed.ts
│   │   ├── auth/
│   │   │   └── verifySession.ts
│   │   ├── cleanRedirect.ts
│   │   ├── cn.ts
│   │   ├── countryCodeList.ts
│   │   ├── countryCodeToFlagEmoji.ts
│   │   ├── dataSize.ts
│   │   ├── docker.ts
│   │   ├── durationToMs.ts
│   │   ├── formatDeviceFingerprint.ts
│   │   ├── getSevenDaysAgo.ts
│   │   ├── getUserDisplayName.ts
│   │   ├── internalRedirect.ts
│   │   ├── parseHostTarget.ts
│   │   ├── pullEnv.ts
│   │   ├── queries.ts
│   │   ├── replacePlaceholder.ts
│   │   ├── shareLinks.ts
│   │   ├── sortColumn.ts
│   │   ├── subdomain-utils.ts
│   │   ├── themeColors.ts
│   │   ├── timeAgoFormatter.ts
│   │   ├── types/
│   │   │   ├── env.ts
│   │   │   └── sort.ts
│   │   ├── validateLocalPath.ts
│   │   ├── wait.ts
│   │   └── wireguard.ts
│   ├── middleware.ts
│   ├── providers/
│   │   ├── ApiKeyProvider.tsx
│   │   ├── ClientProvider.tsx
│   │   ├── DomainProvider.tsx
│   │   ├── EnvProvider.tsx
│   │   ├── LicenseStatusProvider.tsx
│   │   ├── OrgProvider.tsx
│   │   ├── OrgUserProvider.tsx
│   │   ├── RemoteExitNodeProvider.tsx
│   │   ├── ResourceProvider.tsx
│   │   ├── SiteProvider.tsx
│   │   ├── SubscriptionStatusProvider.tsx
│   │   ├── SupporterStatusProvider.tsx
│   │   ├── ThemeDataProvider.tsx
│   │   ├── ThemeProvider.tsx
│   │   └── UserProvider.tsx
│   ├── services/
│   │   └── locale.ts
│   └── types/
│       ├── canvas-confetti.d.ts
│       └── tanstack-query.d.ts
├── test/
│   └── assert.ts
├── tsconfig.enterprise.json
├── tsconfig.oss.json
└── tsconfig.saas.json
Download .txt
Showing preview only (254K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (2674 symbols across 1004 files)

FILE: cli/commands/clearExitNodes.ts
  type ClearExitNodesArgs (line 5) | type ClearExitNodesArgs = { };

FILE: cli/commands/clearLicenseKeys.ts
  type ClearLicenseKeysArgs (line 5) | type ClearLicenseKeysArgs = { };

FILE: cli/commands/deleteClient.ts
  type DeleteClientArgs (line 5) | type DeleteClientArgs = {

FILE: cli/commands/generateOrgCaKeys.ts
  type GenerateOrgCaKeysArgs (line 10) | type GenerateOrgCaKeysArgs = {

FILE: cli/commands/resetUserSecurityKeys.ts
  type ResetUserSecurityKeysArgs (line 5) | type ResetUserSecurityKeysArgs = {

FILE: cli/commands/rotateServerSecret.ts
  type RotateServerSecretArgs (line 9) | type RotateServerSecretArgs = {
  type IdpUpdate (line 139) | type IdpUpdate = {
  type LicenseKeyUpdate (line 145) | type LicenseKeyUpdate = {

FILE: cli/commands/setAdminCredentials.ts
  type SetAdminCredentialsArgs (line 12) | type SetAdminCredentialsArgs = {
  function invalidateAllSessions (line 115) | async function invalidateAllSessions(userId: string): Promise<void> {
  method read (line 136) | read(bytes: Uint8Array): void {
  function generateId (line 141) | function generateId(length: number): string {

FILE: esbuild.mjs
  function getPackagePaths (line 52) | function getPackagePaths() {
  function privateImportGuardPlugin (line 64) | function privateImportGuardPlugin() {
  function dynamicImportGuardPlugin (line 125) | function dynamicImportGuardPlugin() {
  function dynamicImportSwitcherPlugin (line 186) | function dynamicImportSwitcherPlugin(buildValue) {

FILE: install/config.go
  type TraefikConfig (line 14) | type TraefikConfig struct
  type DynamicConfig (line 32) | type DynamicConfig struct
  type TraefikConfigValues (line 41) | type TraefikConfigValues struct
  type AppConfig (line 48) | type AppConfig struct
  type AppConfigValues (line 55) | type AppConfigValues struct
  function ReadTraefikConfig (line 61) | func ReadTraefikConfig(mainConfigPath string) (*TraefikConfigValues, err...
  function ReadAppConfig (line 82) | func ReadAppConfig(configPath string) (*AppConfigValues, error) {
  function findPattern (line 103) | func findPattern(s, pattern string) int {
  function copyDockerService (line 107) | func copyDockerService(sourceFile, destFile, serviceName string) error {
  function backupConfig (line 171) | func backupConfig() error {
  function MarshalYAMLWithIndent (line 190) | func MarshalYAMLWithIndent(data any, indent int) ([]byte, error) {
  function replaceInFile (line 203) | func replaceInFile(filepath, oldStr, newStr string) error {
  function CheckAndAddTraefikLogVolume (line 222) | func CheckAndAddTraefikLogVolume(composePath string) error {
  function MergeYAML (line 283) | func MergeYAML(baseFile, overlayFile string) error {
  function mergeMap (line 326) | func mergeMap(base, overlay map[string]any) map[string]any {

FILE: install/containers.go
  function waitForContainer (line 15) | func waitForContainer(containerName string, containerType SupportedConta...
  function installDocker (line 43) | func installDocker() error {
  function startDockerService (line 146) | func startDockerService() error {
  function isDockerInstalled (line 161) | func isDockerInstalled() bool {
  function isPodmanInstalled (line 165) | func isPodmanInstalled() bool {
  function isContainerInstalled (line 169) | func isContainerInstalled(container string) bool {
  function isUserInDockerGroup (line 177) | func isUserInDockerGroup() bool {
  function isDockerRunning (line 206) | func isDockerRunning() bool {
  function isPodmanRunning (line 214) | func isPodmanRunning() bool {
  function detectContainerType (line 224) | func detectContainerType() SupportedContainer {
  function executeDockerComposeCommandWithArgs (line 256) | func executeDockerComposeCommandWithArgs(args ...string) error {
  function pullContainers (line 288) | func pullContainers(containerType SupportedContainer) error {
  function startContainers (line 310) | func startContainers(containerType SupportedContainer) error {
  function stopContainers (line 333) | func stopContainers(containerType SupportedContainer) error {
  function restartContainer (line 355) | func restartContainer(container string, containerType SupportedContainer...

FILE: install/crowdsec.go
  function installCrowdsec (line 14) | func installCrowdsec(config Config) error {
  function checkIsCrowdsecInstalledInCompose (line 111) | func checkIsCrowdsecInstalledInCompose() bool {
  function GetCrowdSecAPIKey (line 122) | func GetCrowdSecAPIKey(containerType SupportedContainer) (string, error) {
  function checkIfTextInFile (line 146) | func checkIfTextInFile(file, text string) bool {
  function CheckAndAddCrowdsecDependency (line 157) | func CheckAndAddCrowdsecDependency(composePath string) error {

FILE: install/input.go
  function isAccessibleMode (line 18) | func isAccessibleMode() bool {
  function handleAbort (line 35) | func handleAbort(err error) {
  function runField (line 43) | func runField(field huh.Field) error {
  function readString (line 51) | func readString(prompt string, defaultValue string) string {
  function readStringNoDefault (line 88) | func readStringNoDefault(prompt string) string {
  function readPassword (line 115) | func readPassword(prompt string) string {
  function readBool (line 143) | func readBool(prompt string, defaultValue bool) bool {
  function readBoolNoDefault (line 167) | func readBoolNoDefault(prompt string) bool {
  function readInt (line 191) | func readInt(prompt string, defaultValue int) int {

FILE: install/main.go
  function loadVersions (line 29) | func loadVersions(config *Config) {
  type Config (line 38) | type Config struct
  type SupportedContainer (line 61) | type SupportedContainer
  constant Docker (line 64) | Docker    SupportedContainer = "docker"
  constant Podman (line 65) | Podman    SupportedContainer = "podman"
  constant Undefined (line 66) | Undefined SupportedContainer = "undefined"
  function main (line 69) | func main() {
  function podmanOrDocker (line 290) | func podmanOrDocker() SupportedContainer {
  function collectUserInput (line 358) | func collectUserInput() Config {
  function createConfigFiles (line 418) | func createConfigFiles(config Config) error {
  function copyFile (line 502) | func copyFile(src, dst string) error {
  function moveFile (line 519) | func moveFile(src, dst string) error {
  function printSetupToken (line 527) | func printSetupToken(containerType SupportedContainer, dashboardDomain s...
  function showSetupTokenInstructions (line 579) | func showSetupTokenInstructions(containerType SupportedContainer, dashbo...
  function generateRandomSecretKey (line 616) | func generateRandomSecretKey() string {
  function getPublicIP (line 625) | func getPublicIP() string {
  function run (line 652) | func run(name string, args ...string) error {
  function checkPortsAvailable (line 659) | func checkPortsAvailable(port int) error {
  function downloadMaxMindDatabase (line 674) | func downloadMaxMindDatabase() error {

FILE: install/theme.go
  function ThemePangolin (line 23) | func ThemePangolin() *huh.Theme {

FILE: server/apiServer.ts
  function createApiServer (line 29) | function createApiServer() {

FILE: server/auth/actions.ts
  type ActionsEnum (line 8) | enum ActionsEnum {
  function checkUserActionPermission (line 139) | async function checkUserActionPermission(

FILE: server/auth/canUserAccessResource.ts
  function canUserAccessResource (line 5) | async function canUserAccessResource({

FILE: server/auth/canUserAccessSiteResource.ts
  function canUserAccessSiteResource (line 5) | async function canUserAccessSiteResource({

FILE: server/auth/checkValidInvite.ts
  function checkValidInvite (line 7) | async function checkValidInvite({

FILE: server/auth/password.ts
  function verifyPassword (line 3) | async function verifyPassword(
  function hashPassword (line 16) | async function hashPassword(password: string): Promise<string> {

FILE: server/auth/resourceOtp.ts
  function sendResourceOtpEmail (line 12) | async function sendResourceOtpEmail(
  function generateResourceOtpCode (line 35) | async function generateResourceOtpCode(
  function isValidOtp (line 63) | async function isValidOtp(

FILE: server/auth/sendEmailVerificationCode.ts
  function sendEmailVerificationCode (line 10) | async function sendEmailVerificationCode(
  function generateEmailVerificationCode (line 30) | async function generateEmailVerificationCode(

FILE: server/auth/sessions/app.ts
  constant SESSION_COOKIE_NAME (line 21) | const SESSION_COOKIE_NAME =
  constant SESSION_COOKIE_EXPIRES (line 23) | const SESSION_COOKIE_EXPIRES =
  constant COOKIE_DOMAIN (line 28) | const COOKIE_DOMAIN = config.getRawConfig().app.dashboard_url
  function generateSessionToken (line 32) | function generateSessionToken(): string {
  function createSession (line 39) | async function createSession(
  function validateSessionToken (line 58) | async function validateSessionToken(
  function invalidateSession (line 106) | async function invalidateSession(sessionId: string): Promise<void> {
  function invalidateAllSessions (line 119) | async function invalidateAllSessions(userId: string): Promise<void> {
  function serializeSessionCookie (line 139) | function serializeSessionCookie(
  function createBlankSessionTokenCookie (line 151) | function createBlankSessionTokenCookie(isSecure: boolean): string {
  method read (line 160) | read(bytes: Uint8Array): void {
  function generateId (line 165) | function generateId(length: number): string {
  function generateIdFromEntropySize (line 170) | function generateIdFromEntropySize(size: number): string {
  type SessionValidationResult (line 175) | type SessionValidationResult =

FILE: server/auth/sessions/newt.ts
  constant EXPIRES (line 7) | const EXPIRES = 1000 * 60 * 60 * 24 * 30;
  function createNewtSession (line 9) | async function createNewtSession(
  function validateNewtSessionToken (line 25) | async function validateNewtSessionToken(
  function invalidateNewtSession (line 58) | async function invalidateNewtSession(sessionId: string): Promise<void> {
  function invalidateAllNewtSessions (line 62) | async function invalidateAllNewtSessions(newtId: string): Promise<void> {
  type SessionValidationResult (line 66) | type SessionValidationResult =

FILE: server/auth/sessions/olm.ts
  constant EXPIRES (line 7) | const EXPIRES = 1000 * 60 * 60 * 24 * 30;
  function createOlmSession (line 9) | async function createOlmSession(
  function validateOlmSessionToken (line 25) | async function validateOlmSessionToken(
  function invalidateOlmSession (line 58) | async function invalidateOlmSession(sessionId: string): Promise<void> {
  function invalidateAllOlmSessions (line 62) | async function invalidateAllOlmSessions(olmId: string): Promise<void> {
  type SessionValidationResult (line 66) | type SessionValidationResult =

FILE: server/auth/sessions/resource.ts
  constant SESSION_COOKIE_NAME (line 8) | const SESSION_COOKIE_NAME =
  constant SESSION_COOKIE_EXPIRES (line 10) | const SESSION_COOKIE_EXPIRES =
  function createResourceSession (line 13) | async function createResourceSession(opts: {
  function validateResourceSessionToken (line 62) | async function validateResourceSessionToken(
  function invalidateResourceSession (line 114) | async function invalidateResourceSession(
  function invalidateAllSessions (line 122) | async function invalidateAllSessions(
  function serializeResourceSessionCookie (line 169) | function serializeResourceSessionCookie(
  function createBlankResourceSessionTokenCookie (line 190) | function createBlankResourceSessionTokenCookie(
  type ResourceSessionValidationResult (line 202) | type ResourceSessionValidationResult = {

FILE: server/auth/sessions/verifySession.ts
  function verifySession (line 7) | async function verifySession(req: Request, forceLogin?: boolean) {

FILE: server/auth/totp.ts
  function verifyTotpCode (line 9) | async function verifyTotpCode(
  function verifyBackUpCode (line 29) | async function verifyBackUpCode(

FILE: server/auth/unauthorizedResponse.ts
  function unauthorized (line 4) | function unauthorized(msg?: string) {

FILE: server/auth/verifyResourceAccessToken.ts
  function verifyResourceAccessToken (line 14) | async function verifyResourceAccessToken({

FILE: server/cleanup.ts
  function cleanup (line 5) | async function cleanup() {
  function initCleanup (line 13) | async function initCleanup() {

FILE: server/db/asns.ts
  constant MAJOR_ASNS (line 4) | const MAJOR_ASNS = [

FILE: server/db/countries.ts
  constant COUNTRIES (line 1) | const COUNTRIES = [

FILE: server/db/names.ts
  function getUniqueClientName (line 37) | async function getUniqueClientName(orgId: string): Promise<string> {
  function getUniqueSiteName (line 56) | async function getUniqueSiteName(orgId: string): Promise<string> {
  function getUniqueResourceName (line 75) | async function getUniqueResourceName(orgId: string): Promise<string> {
  function getUniqueSiteResourceName (line 110) | async function getUniqueSiteResourceName(
  function getUniqueExitNodeEndpointName (line 147) | async function getUniqueExitNodeEndpointName(): Promise<string> {
  function generateName (line 168) | function generateName(): string {
  function getMacDeviceName (line 181) | function getMacDeviceName(macIdentifier?: string | null): string | null {
  function getIosDeviceName (line 188) | function getIosDeviceName(iosIdentifier?: string | null): string | null {
  function getUserDeviceName (line 195) | function getUserDeviceName(

FILE: server/db/pg/driver.ts
  function createDb (line 6) | function createDb() {
  type Transaction (line 85) | type Transaction = Parameters<

FILE: server/db/pg/logsDriver.ts
  function createLogsDb (line 8) | function createLogsDb() {

FILE: server/db/pg/safeRead.ts
  function safeRead (line 9) | async function safeRead<T>(

FILE: server/db/pg/schema/privateSchema.ts
  type Approval (line 339) | type Approval = InferSelectModel<typeof approvals>;
  type Limit (line 340) | type Limit = InferSelectModel<typeof limits>;
  type Account (line 341) | type Account = InferSelectModel<typeof account>;
  type Certificate (line 342) | type Certificate = InferSelectModel<typeof certificates>;
  type DnsChallenge (line 343) | type DnsChallenge = InferSelectModel<typeof dnsChallenge>;
  type Customer (line 344) | type Customer = InferSelectModel<typeof customers>;
  type Subscription (line 345) | type Subscription = InferSelectModel<typeof subscriptions>;
  type SubscriptionItem (line 346) | type SubscriptionItem = InferSelectModel<typeof subscriptionItems>;
  type Usage (line 347) | type Usage = InferSelectModel<typeof usage>;
  type UsageLimit (line 348) | type UsageLimit = InferSelectModel<typeof limits>;
  type AccountDomain (line 349) | type AccountDomain = InferSelectModel<typeof accountDomains>;
  type UsageNotification (line 350) | type UsageNotification = InferSelectModel<typeof usageNotifications>;
  type RemoteExitNode (line 351) | type RemoteExitNode = InferSelectModel<typeof remoteExitNodes>;
  type RemoteExitNodeSession (line 352) | type RemoteExitNodeSession = InferSelectModel<
  type ExitNodeOrg (line 355) | type ExitNodeOrg = InferSelectModel<typeof exitNodeOrgs>;
  type LoginPage (line 356) | type LoginPage = InferSelectModel<typeof loginPage>;
  type LoginPageBranding (line 357) | type LoginPageBranding = InferSelectModel<typeof loginPageBranding>;
  type ActionAuditLog (line 358) | type ActionAuditLog = InferSelectModel<typeof actionAuditLog>;
  type AccessAuditLog (line 359) | type AccessAuditLog = InferSelectModel<typeof accessAuditLog>;

FILE: server/db/pg/schema/schema.ts
  type Org (line 1014) | type Org = InferSelectModel<typeof orgs>;
  type User (line 1015) | type User = InferSelectModel<typeof users>;
  type Site (line 1016) | type Site = InferSelectModel<typeof sites>;
  type Resource (line 1017) | type Resource = InferSelectModel<typeof resources>;
  type ExitNode (line 1018) | type ExitNode = InferSelectModel<typeof exitNodes>;
  type Target (line 1019) | type Target = InferSelectModel<typeof targets>;
  type Session (line 1020) | type Session = InferSelectModel<typeof sessions>;
  type Newt (line 1021) | type Newt = InferSelectModel<typeof newts>;
  type NewtSession (line 1022) | type NewtSession = InferSelectModel<typeof newtSessions>;
  type EmailVerificationCode (line 1023) | type EmailVerificationCode = InferSelectModel<
  type TwoFactorBackupCode (line 1026) | type TwoFactorBackupCode = InferSelectModel<typeof twoFactorBackupCodes>;
  type PasswordResetToken (line 1027) | type PasswordResetToken = InferSelectModel<typeof passwordResetTokens>;
  type Role (line 1028) | type Role = InferSelectModel<typeof roles>;
  type Action (line 1029) | type Action = InferSelectModel<typeof actions>;
  type RoleAction (line 1030) | type RoleAction = InferSelectModel<typeof roleActions>;
  type UserAction (line 1031) | type UserAction = InferSelectModel<typeof userActions>;
  type RoleSite (line 1032) | type RoleSite = InferSelectModel<typeof roleSites>;
  type UserSite (line 1033) | type UserSite = InferSelectModel<typeof userSites>;
  type RoleResource (line 1034) | type RoleResource = InferSelectModel<typeof roleResources>;
  type UserResource (line 1035) | type UserResource = InferSelectModel<typeof userResources>;
  type UserInvite (line 1036) | type UserInvite = InferSelectModel<typeof userInvites>;
  type UserOrg (line 1037) | type UserOrg = InferSelectModel<typeof userOrgs>;
  type ResourceSession (line 1038) | type ResourceSession = InferSelectModel<typeof resourceSessions>;
  type ResourcePincode (line 1039) | type ResourcePincode = InferSelectModel<typeof resourcePincode>;
  type ResourcePassword (line 1040) | type ResourcePassword = InferSelectModel<typeof resourcePassword>;
  type ResourceHeaderAuth (line 1041) | type ResourceHeaderAuth = InferSelectModel<typeof resourceHeaderAuth>;
  type ResourceHeaderAuthExtendedCompatibility (line 1042) | type ResourceHeaderAuthExtendedCompatibility = InferSelectModel<
  type ResourceOtp (line 1045) | type ResourceOtp = InferSelectModel<typeof resourceOtp>;
  type ResourceAccessToken (line 1046) | type ResourceAccessToken = InferSelectModel<typeof resourceAccessToken>;
  type ResourceWhitelist (line 1047) | type ResourceWhitelist = InferSelectModel<typeof resourceWhitelist>;
  type VersionMigration (line 1048) | type VersionMigration = InferSelectModel<typeof versionMigrations>;
  type ResourceRule (line 1049) | type ResourceRule = InferSelectModel<typeof resourceRules>;
  type Domain (line 1050) | type Domain = InferSelectModel<typeof domains>;
  type SupporterKey (line 1051) | type SupporterKey = InferSelectModel<typeof supporterKey>;
  type Idp (line 1052) | type Idp = InferSelectModel<typeof idp>;
  type ApiKey (line 1053) | type ApiKey = InferSelectModel<typeof apiKeys>;
  type ApiKeyAction (line 1054) | type ApiKeyAction = InferSelectModel<typeof apiKeyActions>;
  type ApiKeyOrg (line 1055) | type ApiKeyOrg = InferSelectModel<typeof apiKeyOrg>;
  type Client (line 1056) | type Client = InferSelectModel<typeof clients>;
  type ClientSite (line 1057) | type ClientSite = InferSelectModel<typeof clientSitesAssociationsCache>;
  type Olm (line 1058) | type Olm = InferSelectModel<typeof olms>;
  type OlmSession (line 1059) | type OlmSession = InferSelectModel<typeof olmSessions>;
  type UserClient (line 1060) | type UserClient = InferSelectModel<typeof userClients>;
  type RoleClient (line 1061) | type RoleClient = InferSelectModel<typeof roleClients>;
  type OrgDomains (line 1062) | type OrgDomains = InferSelectModel<typeof orgDomains>;
  type SiteResource (line 1063) | type SiteResource = InferSelectModel<typeof siteResources>;
  type SetupToken (line 1064) | type SetupToken = InferSelectModel<typeof setupTokens>;
  type HostMeta (line 1065) | type HostMeta = InferSelectModel<typeof hostMeta>;
  type TargetHealthCheck (line 1066) | type TargetHealthCheck = InferSelectModel<typeof targetHealthCheck>;
  type IdpOidcConfig (line 1067) | type IdpOidcConfig = InferSelectModel<typeof idpOidcConfig>;
  type Blueprint (line 1068) | type Blueprint = InferSelectModel<typeof blueprints>;
  type LicenseKey (line 1069) | type LicenseKey = InferSelectModel<typeof licenseKey>;
  type SecurityKey (line 1070) | type SecurityKey = InferSelectModel<typeof securityKeys>;
  type WebauthnChallenge (line 1071) | type WebauthnChallenge = InferSelectModel<typeof webauthnChallenge>;
  type DeviceWebAuthCode (line 1072) | type DeviceWebAuthCode = InferSelectModel<typeof deviceWebAuthCodes>;
  type RequestAuditLog (line 1073) | type RequestAuditLog = InferSelectModel<typeof requestAuditLog>;
  type RoundTripMessageTracker (line 1074) | type RoundTripMessageTracker = InferSelectModel<

FILE: server/db/queries/verifySessionQueries.ts
  type ResourceWithAuth (line 23) | type ResourceWithAuth = {
  type UserSessionWithUser (line 32) | type UserSessionWithUser = {
  function getResourceByDomain (line 40) | async function getResourceByDomain(
  function getUserSessionWithUser (line 87) | async function getUserSessionWithUser(
  function getUserOrgRole (line 109) | async function getUserOrgRole(userId: string, orgId: string) {
  function getRoleResourceAccess (line 130) | async function getRoleResourceAccess(
  function getUserResourceAccess (line 151) | async function getUserResourceAccess(
  function getResourceRules (line 172) | async function getResourceRules(
  function getOrgLoginPage (line 186) | async function getOrgLoginPage(

FILE: server/db/sqlite/driver.ts
  function createDb (line 14) | function createDb() {
  type Transaction (line 24) | type Transaction = Parameters<
  function checkFileExists (line 28) | function checkFileExists(filePath: string): boolean {
  function bootstrapVolume (line 37) | function bootstrapVolume() {

FILE: server/db/sqlite/safeRead.ts
  function safeRead (line 7) | async function safeRead<T>(

FILE: server/db/sqlite/schema/privateSchema.ts
  type Approval (line 330) | type Approval = InferSelectModel<typeof approvals>;
  type Limit (line 331) | type Limit = InferSelectModel<typeof limits>;
  type Account (line 332) | type Account = InferSelectModel<typeof account>;
  type Certificate (line 333) | type Certificate = InferSelectModel<typeof certificates>;
  type DnsChallenge (line 334) | type DnsChallenge = InferSelectModel<typeof dnsChallenge>;
  type Customer (line 335) | type Customer = InferSelectModel<typeof customers>;
  type Subscription (line 336) | type Subscription = InferSelectModel<typeof subscriptions>;
  type SubscriptionItem (line 337) | type SubscriptionItem = InferSelectModel<typeof subscriptionItems>;
  type Usage (line 338) | type Usage = InferSelectModel<typeof usage>;
  type UsageLimit (line 339) | type UsageLimit = InferSelectModel<typeof limits>;
  type AccountDomain (line 340) | type AccountDomain = InferSelectModel<typeof accountDomains>;
  type UsageNotification (line 341) | type UsageNotification = InferSelectModel<typeof usageNotifications>;
  type RemoteExitNode (line 342) | type RemoteExitNode = InferSelectModel<typeof remoteExitNodes>;
  type RemoteExitNodeSession (line 343) | type RemoteExitNodeSession = InferSelectModel<
  type ExitNodeOrg (line 346) | type ExitNodeOrg = InferSelectModel<typeof exitNodeOrgs>;
  type LoginPage (line 347) | type LoginPage = InferSelectModel<typeof loginPage>;
  type LoginPageBranding (line 348) | type LoginPageBranding = InferSelectModel<typeof loginPageBranding>;
  type ActionAuditLog (line 349) | type ActionAuditLog = InferSelectModel<typeof actionAuditLog>;
  type AccessAuditLog (line 350) | type AccessAuditLog = InferSelectModel<typeof accessAuditLog>;

FILE: server/db/sqlite/schema/schema.ts
  type Org (line 1111) | type Org = InferSelectModel<typeof orgs>;
  type User (line 1112) | type User = InferSelectModel<typeof users>;
  type Site (line 1113) | type Site = InferSelectModel<typeof sites>;
  type Resource (line 1114) | type Resource = InferSelectModel<typeof resources>;
  type ExitNode (line 1115) | type ExitNode = InferSelectModel<typeof exitNodes>;
  type Target (line 1116) | type Target = InferSelectModel<typeof targets>;
  type Session (line 1117) | type Session = InferSelectModel<typeof sessions>;
  type Newt (line 1118) | type Newt = InferSelectModel<typeof newts>;
  type NewtSession (line 1119) | type NewtSession = InferSelectModel<typeof newtSessions>;
  type Olm (line 1120) | type Olm = InferSelectModel<typeof olms>;
  type OlmSession (line 1121) | type OlmSession = InferSelectModel<typeof olmSessions>;
  type EmailVerificationCode (line 1122) | type EmailVerificationCode = InferSelectModel<
  type TwoFactorBackupCode (line 1125) | type TwoFactorBackupCode = InferSelectModel<typeof twoFactorBackupCodes>;
  type PasswordResetToken (line 1126) | type PasswordResetToken = InferSelectModel<typeof passwordResetTokens>;
  type Role (line 1127) | type Role = InferSelectModel<typeof roles>;
  type Action (line 1128) | type Action = InferSelectModel<typeof actions>;
  type RoleAction (line 1129) | type RoleAction = InferSelectModel<typeof roleActions>;
  type UserAction (line 1130) | type UserAction = InferSelectModel<typeof userActions>;
  type RoleSite (line 1131) | type RoleSite = InferSelectModel<typeof roleSites>;
  type UserSite (line 1132) | type UserSite = InferSelectModel<typeof userSites>;
  type RoleResource (line 1133) | type RoleResource = InferSelectModel<typeof roleResources>;
  type UserResource (line 1134) | type UserResource = InferSelectModel<typeof userResources>;
  type UserInvite (line 1135) | type UserInvite = InferSelectModel<typeof userInvites>;
  type UserOrg (line 1136) | type UserOrg = InferSelectModel<typeof userOrgs>;
  type ResourceSession (line 1137) | type ResourceSession = InferSelectModel<typeof resourceSessions>;
  type ResourcePincode (line 1138) | type ResourcePincode = InferSelectModel<typeof resourcePincode>;
  type ResourcePassword (line 1139) | type ResourcePassword = InferSelectModel<typeof resourcePassword>;
  type ResourceHeaderAuth (line 1140) | type ResourceHeaderAuth = InferSelectModel<typeof resourceHeaderAuth>;
  type ResourceHeaderAuthExtendedCompatibility (line 1141) | type ResourceHeaderAuthExtendedCompatibility = InferSelectModel<
  type ResourceOtp (line 1144) | type ResourceOtp = InferSelectModel<typeof resourceOtp>;
  type ResourceAccessToken (line 1145) | type ResourceAccessToken = InferSelectModel<typeof resourceAccessToken>;
  type ResourceWhitelist (line 1146) | type ResourceWhitelist = InferSelectModel<typeof resourceWhitelist>;
  type VersionMigration (line 1147) | type VersionMigration = InferSelectModel<typeof versionMigrations>;
  type ResourceRule (line 1148) | type ResourceRule = InferSelectModel<typeof resourceRules>;
  type Domain (line 1149) | type Domain = InferSelectModel<typeof domains>;
  type DnsRecord (line 1150) | type DnsRecord = InferSelectModel<typeof dnsRecords>;
  type Client (line 1151) | type Client = InferSelectModel<typeof clients>;
  type ClientSite (line 1152) | type ClientSite = InferSelectModel<typeof clientSitesAssociationsCache>;
  type RoleClient (line 1153) | type RoleClient = InferSelectModel<typeof roleClients>;
  type UserClient (line 1154) | type UserClient = InferSelectModel<typeof userClients>;
  type SupporterKey (line 1155) | type SupporterKey = InferSelectModel<typeof supporterKey>;
  type Idp (line 1156) | type Idp = InferSelectModel<typeof idp>;
  type ApiKey (line 1157) | type ApiKey = InferSelectModel<typeof apiKeys>;
  type ApiKeyAction (line 1158) | type ApiKeyAction = InferSelectModel<typeof apiKeyActions>;
  type ApiKeyOrg (line 1159) | type ApiKeyOrg = InferSelectModel<typeof apiKeyOrg>;
  type SiteResource (line 1160) | type SiteResource = InferSelectModel<typeof siteResources>;
  type OrgDomains (line 1161) | type OrgDomains = InferSelectModel<typeof orgDomains>;
  type SetupToken (line 1162) | type SetupToken = InferSelectModel<typeof setupTokens>;
  type HostMeta (line 1163) | type HostMeta = InferSelectModel<typeof hostMeta>;
  type TargetHealthCheck (line 1164) | type TargetHealthCheck = InferSelectModel<typeof targetHealthCheck>;
  type IdpOidcConfig (line 1165) | type IdpOidcConfig = InferSelectModel<typeof idpOidcConfig>;
  type Blueprint (line 1166) | type Blueprint = InferSelectModel<typeof blueprints>;
  type LicenseKey (line 1167) | type LicenseKey = InferSelectModel<typeof licenseKey>;
  type SecurityKey (line 1168) | type SecurityKey = InferSelectModel<typeof securityKeys>;
  type WebauthnChallenge (line 1169) | type WebauthnChallenge = InferSelectModel<typeof webauthnChallenge>;
  type RequestAuditLog (line 1170) | type RequestAuditLog = InferSelectModel<typeof requestAuditLog>;
  type DeviceWebAuthCode (line 1171) | type DeviceWebAuthCode = InferSelectModel<typeof deviceWebAuthCodes>;
  type RoundTripMessageTracker (line 1172) | type RoundTripMessageTracker = InferSelectModel<

FILE: server/emails/index.ts
  function createEmailClient (line 8) | function createEmailClient() {

FILE: server/emails/sendEmail.ts
  function sendEmail (line 6) | async function sendEmail(

FILE: server/emails/templates/EnterpriseEditionKeyGenerated.tsx
  type EnterpriseEditionKeyGeneratedProps (line 18) | type EnterpriseEditionKeyGeneratedProps = {

FILE: server/emails/templates/NotifyResetPassword.tsx
  type Props (line 14) | interface Props {

FILE: server/emails/templates/NotifyUsageLimitApproaching.tsx
  type Props (line 14) | interface Props {

FILE: server/emails/templates/NotifyUsageLimitReached.tsx
  type Props (line 14) | interface Props {

FILE: server/emails/templates/ResetPasswordCode.tsx
  type Props (line 17) | interface Props {

FILE: server/emails/templates/ResourceOTPCode.tsx
  type ResourceOTPCodeProps (line 16) | interface ResourceOTPCodeProps {

FILE: server/emails/templates/SendInviteLink.tsx
  type SendInviteLinkProps (line 16) | interface SendInviteLinkProps {

FILE: server/emails/templates/SupportEmail.tsx
  type SupportEmailProps (line 11) | interface SupportEmailProps {

FILE: server/emails/templates/TwoFactorAuthNotification.tsx
  type Props (line 14) | interface Props {

FILE: server/emails/templates/VerifyEmailCode.tsx
  type VerifyEmailProps (line 16) | interface VerifyEmailProps {

FILE: server/emails/templates/WelcomeQuickStart.tsx
  type WelcomeQuickStartProps (line 18) | interface WelcomeQuickStartProps {

FILE: server/emails/templates/components/ButtonLink.tsx
  function ButtonLink (line 3) | function ButtonLink({

FILE: server/emails/templates/components/CopyCodeBox.tsx
  constant DEFAULT_HINT (line 3) | const DEFAULT_HINT = "Copy and paste this code when prompted";
  function CopyCodeBox (line 5) | function CopyCodeBox({

FILE: server/emails/templates/components/Email.tsx
  function EmailContainer (line 6) | function EmailContainer({ children }: { children: React.ReactNode }) {
  function EmailLetterHead (line 15) | function EmailLetterHead() {
  function EmailHeading (line 30) | function EmailHeading({ children }: { children: React.ReactNode }) {
  function EmailGreeting (line 40) | function EmailGreeting({ children }: { children: React.ReactNode }) {
  function EmailText (line 51) | function EmailText({
  function EmailSection (line 70) | function EmailSection({
  function EmailFooter (line 83) | function EmailFooter({ children }: { children: React.ReactNode }) {
  function EmailSignature (line 104) | function EmailSignature() {
  function EmailInfoSection (line 117) | function EmailInfoSection({

FILE: server/extendZod.ts
  function extendZod (line 6) | function extendZod() {}

FILE: server/index.ts
  function startServers (line 27) | async function startServers() {
  type Request (line 71) | interface Request {

FILE: server/integrationApiServer.ts
  function createIntegrationApiServer (line 25) | function createIntegrationApiServer() {
  function getOpenApiDocumentation (line 81) | function getOpenApiDocumentation() {

FILE: server/internalServer.ts
  function createInternalServer (line 16) | function createInternalServer() {

FILE: server/lib/asn.ts
  function getAsnForIp (line 4) | async function getAsnForIp(ip: string): Promise<number | undefined> {

FILE: server/lib/billing/createCustomer.ts
  function createCustomer (line 1) | async function createCustomer(

FILE: server/lib/billing/features.ts
  type FeatureId (line 1) | enum FeatureId {
  function getFeatureDisplayName (line 11) | async function getFeatureDisplayName(featureId: FeatureId): Promise<stri...
  function getFeatureMeterId (line 41) | function getFeatureMeterId(featureId: FeatureId): string | undefined {
  function getFeatureIdByMetricId (line 52) | function getFeatureIdByMetricId(
  type FeaturePriceSet (line 60) | type FeaturePriceSet = Partial<Record<FeatureId, string>>;
  function getTier1FeaturePriceSet (line 70) | function getTier1FeaturePriceSet(): FeaturePriceSet {
  function getTier2FeaturePriceSet (line 89) | function getTier2FeaturePriceSet(): FeaturePriceSet {
  function getTier3FeaturePriceSet (line 108) | function getTier3FeaturePriceSet(): FeaturePriceSet {
  function getFeatureIdByPriceId (line 119) | function getFeatureIdByPriceId(priceId: string): FeatureId | undefined {

FILE: server/lib/billing/getLineItems.ts
  function getLineItems (line 5) | async function getLineItems(

FILE: server/lib/billing/getOrgTierData.ts
  function getOrgTierData (line 1) | async function getOrgTierData(

FILE: server/lib/billing/licenses.ts
  type LicenseId (line 1) | enum LicenseId {
  type LicensePriceSet (line 6) | type LicensePriceSet = {
  function getLicensePriceSet (line 23) | function getLicensePriceSet(

FILE: server/lib/billing/limitSet.ts
  type LimitSet (line 3) | type LimitSet = Partial<{

FILE: server/lib/billing/limitsService.ts
  class LimitService (line 7) | class LimitService {
    method applyLimitSetToOrg (line 8) | async applyLimitSetToOrg(orgId: string, limitSet: LimitSet): Promise<v...
    method getOrgLimit (line 39) | async getOrgLimit(

FILE: server/lib/billing/tierMatrix.ts
  type TierFeature (line 3) | enum TierFeature {

FILE: server/lib/billing/usageService.ts
  function noop (line 17) | function noop() {
  class UsageService (line 24) | class UsageService {
    method constructor (line 26) | constructor() {
    method truncateValue (line 35) | private truncateValue(value: number): number {
    method add (line 39) | public async add(
    method internalAddUsage (line 113) | private async internalAddUsage(
    method getTodayDateString (line 153) | getTodayDateString(): string {
    method getDateString (line 158) | getDateString(date: number): string {
    method updateCount (line 162) | async updateCount(
    method getCustomerId (line 226) | private async getCustomerId(
    method getUsage (line 268) | public async getUsage(
    method getBillingOrg (line 345) | public async getBillingOrg(
    method checkLimitSet (line 375) | public async checkLimitSet(

FILE: server/lib/blueprints/applyBlueprint.ts
  type ApplyBlueprintArgs (line 30) | type ApplyBlueprintArgs = {
  function applyBlueprint (line 38) | async function applyBlueprint({

FILE: server/lib/blueprints/applyNewtDockerBlueprint.ts
  function applyNewtDockerBlueprint (line 8) | async function applyNewtDockerBlueprint(
  function isEmptyObject (line 74) | function isEmptyObject(obj: any) {

FILE: server/lib/blueprints/clientResources.ts
  type ClientResourcesResults (line 19) | type ClientResourcesResults = {
  function updateClientResources (line 24) | async function updateClientResources(

FILE: server/lib/blueprints/parseDockerContainers.ts
  type DockerLabels (line 4) | type DockerLabels = {
  type ParsedObject (line 8) | type ParsedObject = {
  type ContainerPort (line 12) | type ContainerPort = {
  type Container (line 19) | type Container = {
  type Target (line 32) | type Target = {
  type ResourceConfig (line 40) | type ResourceConfig = {
  function getContainerPort (line 45) | function getContainerPort(container: Container): number | null {
  function processContainerLabels (line 54) | function processContainerLabels(containers: Container[]): {
  function processResourceLabels (line 139) | function processResourceLabels(

FILE: server/lib/blueprints/parseDotNotation.ts
  function setNestedProperty (line 1) | function setNestedProperty(obj: any, path: string, value: string): void {
  function convertValue (line 59) | function convertValue(value: string): any {

FILE: server/lib/blueprints/proxyResources.ts
  type ProxyResourcesResults (line 37) | type ProxyResourcesResults = {
  function updateProxyResources (line 43) | async function updateProxyResources(
  function getRuleAction (line 833) | function getRuleAction(input: string) {
  function getRuleValue (line 845) | function getRuleValue(match: string, value: string) {
  function validateRule (line 853) | function validateRule(rule: any) {
  function syncRoleResources (line 869) | async function syncRoleResources(
  function syncUserResources (line 931) | async function syncUserResources(
  function syncWhitelistUsers (line 1001) | async function syncWhitelistUsers(
  function checkIfHealthcheckChanged (line 1053) | function checkIfHealthcheckChanged(
  function checkIfTargetChanged (line 1083) | function checkIfTargetChanged(
  function getDomain (line 1098) | async function getDomain(
  function getDomainId (line 1137) | async function getDomainId(

FILE: server/lib/blueprints/types.ts
  type TargetData (line 51) | type TargetData = z.infer<typeof TargetSchema>;
  function isTargetsOnlyResource (line 307) | function isTargetsOnlyResource(resource: any): boolean {
  type Site (line 532) | type Site = z.infer<typeof SiteSchema>;
  type Target (line 533) | type Target = z.infer<typeof TargetSchema>;
  type Resource (line 534) | type Resource = z.infer<typeof ResourceSchema>;
  type Config (line 535) | type Config = z.infer<typeof ConfigSchema>;

FILE: server/lib/cache.ts
  class AdaptiveCache (line 24) | class AdaptiveCache {
    method set (line 32) | async set(key: string, value: any, ttl?: number): Promise<boolean> {
    method get (line 48) | async get<T = any>(key: string): Promise<T | undefined> {
    method del (line 64) | async del(key: string | string[]): Promise<number> {
    method has (line 85) | async has(key: string): Promise<boolean> {
    method mget (line 95) | async mget<T = any>(keys: string[]): Promise<(T | undefined)[]> {
    method flushAll (line 103) | async flushAll(): Promise<void> {
    method getStats (line 112) | getStats() {
    method getCurrentBackend (line 120) | getCurrentBackend(): "redis" | "local" {
    method take (line 129) | async take<T = any>(key: string): Promise<T | undefined> {
    method getTtl (line 142) | getTtl(key: string): number {
    method keys (line 154) | keys(): string[] {

FILE: server/lib/calculateUserClientsForOrgs.ts
  function calculateUserClientsForOrgs (line 25) | async function calculateUserClientsForOrgs(
  function cleanupOrphanedClients (line 271) | async function cleanupOrphanedClients(

FILE: server/lib/canUserAccessResource.ts
  function canUserAccessResource (line 5) | async function canUserAccessResource({

FILE: server/lib/certificates.ts
  function getValidCertificatesForDomains (line 1) | async function getValidCertificatesForDomains(

FILE: server/lib/checkOrgAccessPolicy.ts
  type CheckOrgAccessPolicyProps (line 3) | type CheckOrgAccessPolicyProps = {
  type CheckOrgAccessPolicyResult (line 12) | type CheckOrgAccessPolicyResult = {
  function enforceResourceSessionLength (line 30) | async function enforceResourceSessionLength(
  function checkOrgAccessPolicy (line 37) | async function checkOrgAccessPolicy(

FILE: server/lib/cleanupLogs.test.ts
  function dateToTimestamp (line 4) | function dateToTimestamp(dateStr: string): number {
  function calculateCutoffTimestampWithNow (line 10) | function calculateCutoffTimestampWithNow(
  function testCalculateCutoffTimestamp (line 27) | function testCalculateCutoffTimestamp() {

FILE: server/lib/cleanupLogs.ts
  function initLogCleanupInterval (line 9) | function initLogCleanupInterval() {
  function calculateCutoffTimestamp (line 71) | function calculateCutoffTimestamp(retentionDays: number): number {

FILE: server/lib/clientVersionChecks.ts
  function canCompress (line 3) | function canCompress(

FILE: server/lib/config.ts
  class Config (line 10) | class Config {
    method constructor (line 19) | constructor() {
    method initServer (line 118) | public async initServer() {
    method checkKeyStatus (line 126) | private async checkKeyStatus() {
    method getRawConfig (line 132) | public getRawConfig() {
    method getNoReplyEmail (line 136) | public getNoReplyEmail(): string | undefined {
    method getDomain (line 142) | public getDomain(domainId: string) {
    method hideSupporterKey (line 149) | public hideSupporterKey(days: number = 7) {
    method isSupporterKeyHidden (line 159) | public isSupporterKeyHidden() {
    method checkSupporterKey (line 169) | public async checkSupporterKey() {
    method getSupporterData (line 229) | public getSupporterData() {

FILE: server/lib/consts.ts
  constant APP_VERSION (line 5) | const APP_VERSION = "1.16.0";
  constant APP_PATH (line 10) | const APP_PATH = path.join("config");

FILE: server/lib/corsWithLoginPage.ts
  function isValidLoginPageDomain (line 8) | async function isValidLoginPageDomain(host: string): Promise<boolean> {
  function corsWithLoginPageSupport (line 25) | function corsWithLoginPageSupport(corsConfig: any) {

FILE: server/lib/crypto.ts
  function encrypt (line 3) | function encrypt(value: string, key: string): string {
  function decrypt (line 8) | function decrypt(encryptedValue: string, key: string): string {

FILE: server/lib/deleteOrg.ts
  type DeleteOrgByIdResult (line 29) | type DeleteOrgByIdResult = {
  function deleteOrgById (line 39) | async function deleteOrgById(
  function sendTerminationMessages (line 225) | function sendTerminationMessages(result: DeleteOrgByIdResult): void {

FILE: server/lib/domainUtils.ts
  type DomainValidationResult (line 7) | type DomainValidationResult =
  function validateAndConstructDomain (line 26) | async function validateAndConstructDomain(

FILE: server/lib/encryption.ts
  function encryptData (line 3) | function encryptData(data: string, key: Buffer): string {
  function decryptData (line 18) | function decryptData(encryptedData: string, key: Buffer): string {

FILE: server/lib/exitNodes/exitNodeComms.ts
  type ExitNodeRequest (line 5) | interface ExitNodeRequest {
  function sendToExitNode (line 19) | async function sendToExitNode(

FILE: server/lib/exitNodes/exitNodes.ts
  function verifyExitNodeOrgAccess (line 6) | async function verifyExitNodeOrgAccess(
  function listExitNodes (line 19) | async function listExitNodes(
  function selectBestExitNode (line 51) | function selectBestExitNode(
  function checkExitNodeOrg (line 62) | async function checkExitNodeOrg(
  function resolveExitNodes (line 70) | async function resolveExitNodes(

FILE: server/lib/exitNodes/getCurrentExitNodeId.ts
  function getCurrentExitNodeId (line 6) | async function getCurrentExitNodeId(): Promise<number> {

FILE: server/lib/exitNodes/subnet.ts
  function getNextAvailableSubnet (line 5) | async function getNextAvailableSubnet(): Promise<string> {

FILE: server/lib/geoip.ts
  function getCountryCodeForIp (line 4) | async function getCountryCodeForIp(

FILE: server/lib/hostMeta.ts
  function setHostMeta (line 7) | async function setHostMeta() {
  function getHostMeta (line 21) | async function getHostMeta() {

FILE: server/lib/idp/generateRedirectUrl.ts
  function generateOidcRedirectUrl (line 5) | async function generateOidcRedirectUrl(

FILE: server/lib/ip.test.ts
  function testFindNextAvailableCidr (line 5) | function testFindNextAvailableCidr() {

FILE: server/lib/ip.ts
  type IPRange (line 9) | interface IPRange {
  type IPVersion (line 14) | type IPVersion = 4 | 6;
  function detectIpVersion (line 19) | function detectIpVersion(ip: string): IPVersion {
  function ipToBigInt (line 26) | function ipToBigInt(ip: string): bigint {
  function bigIntToIp (line 64) | function bigIntToIp(num: bigint, version: IPVersion): string {
  function parseEndpoint (line 122) | function parseEndpoint(
  function formatEndpoint (line 168) | function formatEndpoint(ip: string, port: number): string {
  function cidrToRange (line 181) | function cidrToRange(cidr: string): IPRange {
  function findNextAvailableCidr (line 211) | function findNextAvailableCidr(
  function isIpInCidr (line 290) | function isIpInCidr(ip: string, cidr: string): boolean {
  function doCidrsOverlap (line 311) | function doCidrsOverlap(cidr1: string, cidr2: string): boolean {
  function getNextAvailableClientSubnet (line 325) | async function getNextAvailableClientSubnet(
  function getNextAvailableAliasAddress (line 373) | async function getNextAvailableAliasAddress(
  function getNextAvailableOrgSubnet (line 423) | async function getNextAvailableOrgSubnet(): Promise<string> {
  function generateRemoteSubnets (line 445) | function generateRemoteSubnets(
  type Alias (line 476) | type Alias = { alias: string | null; aliasAddress: string | null };
  function generateAliasConfig (line 478) | function generateAliasConfig(allSiteResources: SiteResource[]): Alias[] {
  type SubnetProxyTarget (line 487) | type SubnetProxyTarget = {
  function generateSubnetProxyTargets (line 499) | function generateSubnetProxyTargets(
  function parsePortRangeString (line 654) | function parsePortRangeString(
  function stripPortFromHost (line 688) | function stripPortFromHost(ip: string, badgerVersion?: string): string {

FILE: server/lib/isLicencedOrSubscribed.ts
  function isLicensedOrSubscribed (line 3) | async function isLicensedOrSubscribed(

FILE: server/lib/isSubscribed.ts
  function isSubscribed (line 3) | async function isSubscribed(

FILE: server/lib/lock.ts
  class LockManager (line 1) | class LockManager {
    method acquireLock (line 8) | async acquireLock(
    method releaseLock (line 19) | async releaseLock(lockKey: string): Promise<void> {}
    method forceReleaseLock (line 25) | async forceReleaseLock(lockKey: string): Promise<void> {}
    method getLockInfo (line 32) | async getLockInfo(lockKey: string): Promise<{
    method extendLock (line 47) | async extendLock(lockKey: string, ttlMs: number): Promise<boolean> {
    method acquireLockWithRetry (line 59) | async acquireLockWithRetry(
    method withLock (line 75) | async withLock<T>(
    method getLockStatistics (line 98) | async getLockStatistics(): Promise<{
    method disconnect (line 108) | async disconnect(): Promise<void> {}

FILE: server/lib/logAccessAudit.ts
  function cleanUpOldLogs (line 1) | async function cleanUpOldLogs(orgId: string, retentionDays: number) {
  function logAccessAudit (line 5) | async function logAccessAudit(data: {

FILE: server/lib/normalizePostAuthPath.ts
  function normalizePostAuthPath (line 5) | function normalizePostAuthPath(path: string | null | undefined): string ...

FILE: server/lib/rateLimitStore.ts
  function createStore (line 3) | function createStore(): Store {

FILE: server/lib/readConfigFile.ts
  function readConfigFile (line 440) | function readConfigFile() {

FILE: server/lib/rebuildClientAssociations.ts
  function getClientSiteResourceAccess (line 46) | async function getClientSiteResourceAccess(
  function rebuildClientAssociationsFromSiteResource (line 145) | async function rebuildClientAssociationsFromSiteResource(
  function handleMessagesForSiteClients (line 350) | async function handleMessagesForSiteClients(
  type PeerDestination (line 503) | interface PeerDestination {
  function updateClientSiteDestinations (line 509) | async function updateClientSiteDestinations(
  function handleSubnetProxyTargetUpdates (line 624) | async function handleSubnetProxyTargetUpdates(
  function rebuildClientAssociationsFromClient (line 772) | async function rebuildClientAssociationsFromClient(
  function handleMessagesForClientSites (line 980) | async function handleMessagesForClientSites(
  function handleMessagesForClientResources (line 1125) | async function handleMessagesForClientResources(

FILE: server/lib/serverIpService.ts
  function fetchServerIp (line 12) | async function fetchServerIp() {
  function getServerIp (line 29) | function getServerIp() {

FILE: server/lib/sshCA.ts
  function encodeString (line 22) | function encodeString(data: Buffer | string): Buffer {
  function encodeUInt32 (line 32) | function encodeUInt32(value: number): Buffer {
  function encodeUInt64 (line 41) | function encodeUInt64(value: bigint): Buffer {
  function decodeString (line 51) | function decodeString(
  function parseOpenSSHPublicKey (line 67) | function parseOpenSSHPublicKey(pubKeyLine: string): {
  function encodeEd25519PublicKey (line 95) | function encodeEd25519PublicKey(publicKey: Buffer): Buffer {
  function formatOpenSSHPublicKey (line 105) | function formatOpenSSHPublicKey(keyBlob: Buffer, comment: string = ""): ...
  type CertificateOptions (line 115) | interface CertificateOptions {
  function buildExtensions (line 137) | function buildExtensions(extensions: string[]): Buffer {
  function buildCriticalOptions (line 154) | function buildCriticalOptions(options: Map<string, string>): Buffer {
  function buildPrincipals (line 169) | function buildPrincipals(principals: string[]): Buffer {
  function extractEd25519PublicKey (line 180) | function extractEd25519PublicKey(keyBlob: Buffer): Buffer {
  type CAKeyPair (line 190) | interface CAKeyPair {
  type SignedCertificate (line 201) | interface SignedCertificate {
  function generateCA (line 233) | function generateCA(comment: string = "pangolin-ssh-ca"): CAKeyPair {
  function getOrgCAKeys (line 269) | async function getOrgCAKeys(
  function signPublicKey (line 328) | function signPublicKey(

FILE: server/lib/stoi.ts
  function stoi (line 1) | function stoi(val: any) {

FILE: server/lib/telemetry.ts
  class TelemetryClient (line 14) | class TelemetryClient {
    method constructor (line 19) | constructor() {
    method startAnalyticsInterval (line 64) | private startAnalyticsInterval() {
    method anon (line 83) | private anon(value: string): string {
    method getSystemStats (line 90) | private async getSystemStats() {
    method sendStartupEvents (line 212) | private async sendStartupEvents() {
    method collectAndSendAnalytics (line 259) | private async collectAndSendAnalytics() {
    method sendTelemetry (line 333) | async sendTelemetry(eventName: string, properties: Record<string, any>) {
    method shutdown (line 349) | shutdown() {
  function initTelemetryClient (line 363) | function initTelemetryClient() {

FILE: server/lib/totp.ts
  function generateBackupCodes (line 3) | async function generateBackupCodes(): Promise<string[]> {

FILE: server/lib/traefik/TraefikConfigManager.ts
  class TraefikConfigManager (line 15) | class TraefikConfigManager {
    method constructor (line 32) | constructor() {}
    method scheduleNextExecution (line 37) | private scheduleNextExecution(): void {
    method start (line 56) | async start(): Promise<void> {
    method stop (line 90) | stop(): void {
    method scanLocalCertificateState (line 108) | private async scanLocalCertificateState(): Promise<
    method shouldFetchCertificates (line 215) | private shouldFetchCertificates(currentDomains: Set<string>): boolean {
    method HandleTraefikConfig (line 338) | public async HandleTraefikConfig(): Promise<void> {
    method internalGetTraefikConfig (line 520) | private async internalGetTraefikConfig(): Promise<{
    method writeTraefikDynamicConfig (line 629) | private async writeTraefikDynamicConfig(traefikConfig: any): Promise<v...
    method updateDynamicConfigFromLocalCerts (line 668) | private async updateDynamicConfigFromLocalCerts(
    method processValidCertificates (line 766) | private async processValidCertificates(
    method shouldUpdateCertificate (line 914) | private async shouldUpdateCertificate(
    method cleanupUnusedCertificates (line 974) | private async cleanupUnusedCertificates(
    method ensureDirectoryExists (line 1069) | private async ensureDirectoryExists(dirPath: string): Promise<void> {
    method fileExists (line 1081) | private async fileExists(filePath: string): Promise<boolean> {
    method forceCertificateRefresh (line 1093) | public async forceCertificateRefresh(): Promise<void> {
    method getStatus (line 1103) | getStatus(): {
  function isDomainCoveredByWildcard (line 1150) | function isDomainCoveredByWildcard(

FILE: server/lib/traefik/getTraefikConfig.ts
  type TargetWithSite (line 23) | type TargetWithSite = Target & {
  function getTraefikConfig (line 41) | async function getTraefikConfig(

FILE: server/lib/traefik/middleware.ts
  function createPathRewriteMiddleware (line 3) | function createPathRewriteMiddleware(
  function escapeRegex (line 138) | function escapeRegex(string: string): string {

FILE: server/lib/traefik/pathEncoding.test.ts
  function sanitize (line 5) | function sanitize(input: string | null | undefined): string | undefined {
  function encodePath (line 16) | function encodePath(path: string | null | undefined): string {
  function oldKeyComputation (line 29) | function oldKeyComputation(
  function newKeyComputation (line 49) | function newKeyComputation(
  function runTests (line 67) | function runTests() {

FILE: server/lib/traefik/traefikConfig.test.ts
  function runTests (line 4) | function runTests() {

FILE: server/lib/traefik/utils.ts
  function sanitize (line 3) | function sanitize(input: string | null | undefined): string | undefined {
  function encodePath (line 29) | function encodePath(path: string | null | undefined): string {
  function validatePathRewriteConfig (line 36) | function validatePathRewriteConfig(

FILE: server/lib/userOrg.ts
  function assignUserToOrg (line 19) | async function assignUserToOrg(
  function removeUserFromOrg (line 56) | async function removeUserFromOrg(

FILE: server/lib/validators.test.ts
  function runTests (line 4) | function runTests() {

FILE: server/lib/validators.ts
  function isValidCIDR (line 4) | function isValidCIDR(cidr: string): boolean {
  function isValidIP (line 10) | function isValidIP(ip: string): boolean {
  function isValidUrlGlobPattern (line 14) | function isValidUrlGlobPattern(pattern: string): boolean {
  function isUrlValid (line 70) | function isUrlValid(url: string | undefined) {
  function isTargetValid (line 84) | function isTargetValid(value: string | undefined) {
  function isValidDomain (line 103) | function isValidDomain(domain: string): boolean {
  function validateHeaders (line 138) | function validateHeaders(headers: string): boolean {
  function isSecondLevelDomain (line 172) | function isSecondLevelDomain(domain: string): boolean {

FILE: server/license/license.ts
  type LicenseKeyType (line 5) | type LicenseKeyType = (typeof keyTypes)[number];
  type LicenseKeyTier (line 8) | type LicenseKeyTier = (typeof keyTiers)[number];
  type LicenseStatus (line 10) | type LicenseStatus = {
  type LicenseKeyCache (line 21) | type LicenseKeyCache = {
  class License (line 33) | class License {
    method constructor (line 36) | constructor(private hostMeta: HostMeta) { }
    method check (line 38) | public async check(): Promise<LicenseStatus> {
    method setServerSecret (line 46) | public setServerSecret(secret: string) {
    method isUnlocked (line 50) | public async isUnlocked() {

FILE: server/middlewares/csrfProtection.ts
  function csrfProtectionMiddleware (line 3) | function csrfProtectionMiddleware(

FILE: server/middlewares/getUserOrgs.ts
  function getUserOrgs (line 8) | async function getUserOrgs(

FILE: server/middlewares/integration/verifyAccessTokenAccess.ts
  function verifyApiKeyAccessTokenAccess (line 8) | async function verifyApiKeyAccessTokenAccess(

FILE: server/middlewares/integration/verifyApiKey.ts
  function verifyApiKey (line 10) | async function verifyApiKey(

FILE: server/middlewares/integration/verifyApiKeyApiKeyAccess.ts
  function verifyApiKeyApiKeyAccess (line 8) | async function verifyApiKeyApiKeyAccess(

FILE: server/middlewares/integration/verifyApiKeyClientAccess.ts
  function verifyApiKeyClientAccess (line 8) | async function verifyApiKeyClientAccess(

FILE: server/middlewares/integration/verifyApiKeyDomainAccess.ts
  function verifyApiKeyDomainAccess (line 7) | async function verifyApiKeyDomainAccess(

FILE: server/middlewares/integration/verifyApiKeyHasAction.ts
  function verifyApiKeyHasAction (line 10) | function verifyApiKeyHasAction(action: ActionsEnum) {

FILE: server/middlewares/integration/verifyApiKeyIdpAccess.ts
  function verifyApiKeyIdpAccess (line 8) | async function verifyApiKeyIdpAccess(

FILE: server/middlewares/integration/verifyApiKeyIsRoot.ts
  function verifyApiKeyIsRoot (line 6) | async function verifyApiKeyIsRoot(

FILE: server/middlewares/integration/verifyApiKeyOrgAccess.ts
  function verifyApiKeyOrgAccess (line 8) | async function verifyApiKeyOrgAccess(

FILE: server/middlewares/integration/verifyApiKeyResourceAccess.ts
  function verifyApiKeyResourceAccess (line 8) | async function verifyApiKeyResourceAccess(

FILE: server/middlewares/integration/verifyApiKeyRoleAccess.ts
  function verifyApiKeyRoleAccess (line 9) | async function verifyApiKeyRoleAccess(

FILE: server/middlewares/integration/verifyApiKeySetResourceClients.ts
  function verifyApiKeySetResourceClients (line 8) | async function verifyApiKeySetResourceClients(

FILE: server/middlewares/integration/verifyApiKeySetResourceUsers.ts
  function verifyApiKeySetResourceUsers (line 8) | async function verifyApiKeySetResourceUsers(

FILE: server/middlewares/integration/verifyApiKeySiteAccess.ts
  function verifyApiKeySiteAccess (line 8) | async function verifyApiKeySiteAccess(

FILE: server/middlewares/integration/verifyApiKeySiteResourceAccess.ts
  function verifyApiKeySiteResourceAccess (line 8) | async function verifyApiKeySiteResourceAccess(

FILE: server/middlewares/integration/verifyApiKeyTargetAccess.ts
  function verifyApiKeyTargetAccess (line 8) | async function verifyApiKeyTargetAccess(

FILE: server/middlewares/integration/verifyApiKeyUserAccess.ts
  function verifyApiKeyUserAccess (line 8) | async function verifyApiKeyUserAccess(

FILE: server/middlewares/logActionAudit.ts
  function logActionAudit (line 4) | function logActionAudit(action: ActionsEnum) {
  function cleanUpOldLogs (line 14) | async function cleanUpOldLogs(orgId: string, retentionDays: number) {

FILE: server/middlewares/logIncoming.ts
  function logIncomingMiddleware (line 4) | function logIncomingMiddleware(

FILE: server/middlewares/notFound.ts
  function notFoundMiddleware (line 5) | function notFoundMiddleware(

FILE: server/middlewares/requestTimeout.ts
  function requestTimeoutMiddleware (line 6) | function requestTimeoutMiddleware(timeoutMs: number = 30000) {

FILE: server/middlewares/verifyAccessTokenAccess.ts
  function verifyAccessTokenAccess (line 10) | async function verifyAccessTokenAccess(

FILE: server/middlewares/verifyAdmin.ts
  function verifyAdmin (line 9) | async function verifyAdmin(

FILE: server/middlewares/verifyApiKeyAccess.ts
  function verifyApiKeyAccess (line 9) | async function verifyApiKeyAccess(

FILE: server/middlewares/verifyClientAccess.ts
  function verifyClientAccess (line 10) | async function verifyClientAccess(

FILE: server/middlewares/verifyDomainAccess.ts
  function verifyDomainAccess (line 9) | async function verifyDomainAccess(

FILE: server/middlewares/verifyIsLoggedInUser.ts
  function verifyIsLoggedInUser (line 5) | async function verifyIsLoggedInUser(

FILE: server/middlewares/verifyLimits.ts
  function verifyLimits (line 7) | async function verifyLimits(

FILE: server/middlewares/verifyOlmAccess.ts
  function verifyOlmAccess (line 7) | async function verifyOlmAccess(

FILE: server/middlewares/verifyOrgAccess.ts
  function verifyOrgAccess (line 9) | async function verifyOrgAccess(

FILE: server/middlewares/verifyResourceAccess.ts
  function verifyResourceAccess (line 9) | async function verifyResourceAccess(

FILE: server/middlewares/verifyRoleAccess.ts
  function verifyRoleAccess (line 10) | async function verifyRoleAccess(

FILE: server/middlewares/verifySetResourceClients.ts
  function verifySetResourceClients (line 9) | async function verifySetResourceClients(

FILE: server/middlewares/verifySetResourceUsers.ts
  function verifySetResourceUsers (line 9) | async function verifySetResourceUsers(

FILE: server/middlewares/verifySiteAccess.ts
  function verifySiteAccess (line 9) | async function verifySiteAccess(

FILE: server/middlewares/verifySiteResourceAccess.ts
  function verifySiteResourceAccess (line 10) | async function verifySiteResourceAccess(

FILE: server/middlewares/verifyTargetAccess.ts
  function verifyTargetAccess (line 10) | async function verifyTargetAccess(

FILE: server/middlewares/verifyUserAccess.ts
  function verifyUserAccess (line 9) | async function verifyUserAccess(

FILE: server/middlewares/verifyUserHasAction.ts
  function verifyUserHasAction (line 8) | function verifyUserHasAction(action: ActionsEnum) {

FILE: server/middlewares/verifyUserInRole.ts
  function verifyUserInRole (line 6) | async function verifyUserInRole(

FILE: server/middlewares/verifyUserIsOrgOwner.ts
  function verifyUserIsOrgOwner (line 8) | async function verifyUserIsOrgOwner(

FILE: server/middlewares/verifyUserIsServerAdmin.ts
  function verifyUserIsServerAdmin (line 5) | async function verifyUserIsServerAdmin(

FILE: server/nextServer.ts
  function createNextServer (line 10) | async function createNextServer() {

FILE: server/openApi.ts
  type OpenAPITags (line 5) | enum OpenAPITags {

FILE: server/private/auth/sessions/remoteExitNode.ts
  constant EXPIRES (line 25) | const EXPIRES = 1000 * 60 * 60 * 24 * 30;
  function createRemoteExitNodeSession (line 27) | async function createRemoteExitNodeSession(
  function validateRemoteExitNodeSessionToken (line 43) | async function validateRemoteExitNodeSessionToken(
  function invalidateRemoteExitNodeSession (line 85) | async function invalidateRemoteExitNodeSession(
  function invalidateAllRemoteExitNodeSessions (line 93) | async function invalidateAllRemoteExitNodeSessions(
  type SessionValidationResult (line 101) | type SessionValidationResult =

FILE: server/private/cleanup.ts
  function cleanup (line 19) | async function cleanup() {
  function initCleanup (line 28) | async function initCleanup() {

FILE: server/private/lib/billing/createCustomer.ts
  function createCustomer (line 19) | async function createCustomer(

FILE: server/private/lib/billing/getOrgTierData.ts
  function getOrgTierData (line 20) | async function getOrgTierData(

FILE: server/private/lib/cache.ts
  class AdaptiveCache (line 25) | class AdaptiveCache {
    method useRedis (line 26) | private useRedis(): boolean {
    method set (line 37) | async set(key: string, value: any, ttl?: number): Promise<boolean> {
    method get (line 71) | async get<T = any>(key: string): Promise<T | undefined> {
    method del (line 104) | async del(key: string | string[]): Promise<number> {
    method has (line 148) | async has(key: string): Promise<boolean> {
    method mget (line 168) | async mget<T = any>(keys: string[]): Promise<(T | undefined)[]> {
    method flushAll (line 196) | async flushAll(): Promise<void> {
    method getStats (line 209) | getStats() {
    method getCurrentBackend (line 217) | getCurrentBackend(): "redis" | "local" {
    method take (line 226) | async take<T = any>(key: string): Promise<T | undefined> {
    method getTtl (line 239) | getTtl(key: string): number {
    method keys (line 256) | keys(): string[] {

FILE: server/private/lib/certificates.ts
  function loadEncryptData (line 23) | function loadEncryptData() {
  type CertificateResult (line 33) | type CertificateResult = {
  function getValidCertificatesForDomains (line 44) | async function getValidCertificatesForDomains(
  function decryptFinalResults (line 180) | function decryptFinalResults(

FILE: server/private/lib/checkOrgAccessPolicy.ts
  function enforceResourceSessionLength (line 24) | function enforceResourceSessionLength(
  function checkOrgAccessPolicy (line 53) | async function checkOrgAccessPolicy(

FILE: server/private/lib/config.ts
  class PrivateConfig (line 23) | class PrivateConfig {
    method constructor (line 32) | constructor() {
    method getRawPrivateConfig (line 139) | public getRawPrivateConfig() {

FILE: server/private/lib/exitNodes/exitNodeComms.ts
  type ExitNodeRequest (line 22) | interface ExitNodeRequest {
  function sendToExitNode (line 36) | async function sendToExitNode(

FILE: server/private/lib/exitNodes/exitNodes.ts
  function checkExitNodeOnlineStatus (line 34) | async function checkExitNodeOnlineStatus(
  function verifyExitNodeOrgAccess (line 111) | async function verifyExitNodeOrgAccess(
  function listExitNodes (line 150) | async function listExitNodes(
  function selectBestExitNode (line 280) | function selectBestExitNode(
  function checkExitNodeOrg (line 355) | async function checkExitNodeOrg(
  function resolveExitNodes (line 378) | async function resolveExitNodes(hostname: string, publicKey: string) {

FILE: server/private/lib/isLicencedOrSubscribed.ts
  function isLicensedOrSubscribed (line 19) | async function isLicensedOrSubscribed(

FILE: server/private/lib/isSubscribed.ts
  function isSubscribed (line 18) | async function isSubscribed(

FILE: server/private/lib/lock.ts
  class LockManager (line 21) | class LockManager {
    method acquireLock (line 28) | async acquireLock(
    method releaseLock (line 110) | async releaseLock(lockKey: string): Promise<void> {
    method forceReleaseLock (line 160) | async forceReleaseLock(lockKey: string): Promise<void> {
    method getLockInfo (line 182) | async getLockInfo(lockKey: string): Promise<{
    method extendLock (line 226) | async extendLock(lockKey: string, ttlMs: number): Promise<boolean> {
    method acquireLockWithRetry (line 279) | async acquireLockWithRetry(
    method withLock (line 317) | async withLock<T>(
    method getLockStatistics (line 344) | async getLockStatistics(): Promise<{
    method disconnect (line 380) | async disconnect(): Promise<void> {

FILE: server/private/lib/logAccessAudit.ts
  function getAccessDays (line 22) | async function getAccessDays(orgId: string): Promise<number> {
  function cleanUpOldLogs (line 51) | async function cleanUpOldLogs(orgId: string, retentionDays: number) {
  function logAccessAudit (line 72) | async function logAccessAudit(data: {
  function getCountryCodeFromIp (line 146) | async function getCountryCodeFromIp(ip: string): Promise<string | undefi...

FILE: server/private/lib/rateLimit.test.ts
  function generateClientId (line 19) | function generateClientId() {
  function runTests (line 23) | async function runTests() {

FILE: server/private/lib/rateLimit.ts
  constant RATE_LIMIT_WINDOW (line 19) | const RATE_LIMIT_WINDOW = 60;
  constant RATE_LIMIT_MAX_REQUESTS (line 20) | const RATE_LIMIT_MAX_REQUESTS = 100;
  constant RATE_LIMIT_PER_MESSAGE_TYPE (line 21) | const RATE_LIMIT_PER_MESSAGE_TYPE = 20;
  constant REDIS_SYNC_THRESHOLD (line 24) | const REDIS_SYNC_THRESHOLD = 15;
  constant REDIS_SYNC_FORCE_INTERVAL (line 25) | const REDIS_SYNC_FORCE_INTERVAL = 30000;
  type RateLimitTracker (line 27) | interface RateLimitTracker {
  type RateLimitResult (line 34) | interface RateLimitResult {
  class RateLimitService (line 41) | class RateLimitService {
    method constructor (line 48) | constructor() {
    method getRateLimitKey (line 68) | private getRateLimitKey(clientId: string): string {
    method getMessageTypeRateLimitKey (line 72) | private getMessageTypeRateLimitKey(
    method cleanupOldTimestamps (line 80) | private async cleanupOldTimestamps(
    method syncRateLimitToRedis (line 125) | private async syncRateLimitToRedis(
    method syncMessageTypeRateLimitToRedis (line 173) | private async syncMessageTypeRateLimitToRedis(
    method initializeLocalTracker (line 229) | private async initializeLocalTracker(
    method initializeMessageTypeTracker (line 282) | private async initializeMessageTypeTracker(
    method checkRateLimit (line 341) | async checkRateLimit(
    method decrementRateLimit (line 448) | async decrementRateLimit(
    method resetKey (line 473) | async resetKey(clientId: string): Promise<void> {
    method cleanupLocalRateLimit (line 505) | private async cleanupLocalRateLimit(): Promise<void> {
    method forceSyncAllPendingData (line 544) | async forceSyncAllPendingData(): Promise<void> {
    method cleanup (line 578) | async cleanup(): Promise<void> {

FILE: server/private/lib/rateLimitStore.ts
  function createStore (line 19) | function createStore(): Store {

FILE: server/private/lib/readConfigFile.ts
  function readPrivateConfigFile (line 205) | function readPrivateConfigFile() {

FILE: server/private/lib/redis.ts
  class RedisManager (line 19) | class RedisManager {
    method constructor (line 44) | constructor() {
    method onReconnection (line 57) | public onReconnection(callback: () => Promise<void>): void {
    method offReconnection (line 62) | public offReconnection(callback: () => Promise<void>): void {
    method triggerReconnectionCallbacks (line 66) | private async triggerReconnectionCallbacks(): Promise<void> {
    method resubscribeToChannels (line 84) | private async resubscribeToChannels(): Promise<void> {
    method getRedisConfig (line 104) | private getRedisConfig(): RedisOptions {
    method getReplicaRedisConfig (line 123) | private getReplicaRedisConfig(): RedisOptions | null {
    method initializeClients (line 150) | private initializeClients(): void {
    method updateOverallHealth (line 337) | private updateOverallHealth(): void {
    method executeWithRetry (line 343) | private async executeWithRetry<T>(
    method startHealthMonitoring (line 401) | private startHealthMonitoring(): void {
    method isRedisEnabled (line 414) | public isRedisEnabled(): boolean {
    method checkRedisHealth (line 418) | private async checkRedisHealth(): Promise<boolean> {
    method getClient (line 495) | public getClient(): Redis {
    method getWriteClient (line 499) | public getWriteClient(): Redis | null {
    method getReadClient (line 503) | public getReadClient(): Redis | null {
    method hasReplicaSupport (line 507) | public hasReplicaSupport(): boolean {
    method getHealthStatus (line 511) | public getHealthStatus(): {
    method set (line 527) | public async set(
    method get (line 549) | public async get(key: string): Promise<string | null> {
    method del (line 569) | public async del(key: string): Promise<boolean> {
    method incr (line 584) | public async incr(key: string): Promise<number> {
    method sadd (line 598) | public async sadd(key: string, member: string): Promise<boolean> {
    method srem (line 613) | public async srem(key: string, member: string): Promise<boolean> {
    method smembers (line 628) | public async smembers(key: string): Promise<string[]> {
    method hset (line 648) | public async hset(
    method hget (line 667) | public async hget(key: string, field: string): Promise<string | null> {
    method hdel (line 687) | public async hdel(key: string, field: string): Promise<boolean> {
    method hgetall (line 702) | public async hgetall(key: string): Promise<Record<string, string>> {
    method publish (line 722) | public async publish(channel: string, message: string): Promise<boolea...
    method subscribe (line 753) | public async subscribe(
    method unsubscribe (line 789) | public async unsubscribe(
    method disconnect (line 825) | public async disconnect(): Promise<void> {

FILE: server/private/lib/redisStore.ts
  class RedisStore (line 29) | class RedisStore implements Store {
    method constructor (line 60) | constructor(
    method init (line 77) | init(options: Options): void {
    method increment (line 90) | async increment(key: string): Promise<IncrementResponse> {
    method decrement (line 126) | async decrement(key: string): Promise<void> {
    method resetKey (line 143) | async resetKey(key: string): Promise<void> {
    method resetAll (line 162) | async resetAll(): Promise<void> {
    method getHits (line 189) | async getHits(
    method shutdown (line 223) | async shutdown(): Promise<void> {

FILE: server/private/lib/traefik/getTraefikConfig.ts
  type TargetWithSite (line 55) | type TargetWithSite = Target & {
  function getTraefikConfig (line 73) | async function getTraefikConfig(

FILE: server/private/license/license.ts
  type ActivateLicenseKeyAPIResponse (line 30) | type ActivateLicenseKeyAPIResponse = {
  type ValidateLicenseAPIResponse (line 40) | type ValidateLicenseAPIResponse = {
  type TokenPayload (line 52) | type TokenPayload = {
  class License (line 62) | class License {
    method constructor (line 87) | constructor(private hostMeta: HostMeta) {
    method listKeys (line 95) | public listKeys(): LicenseKeyCache[] {
    method setServerSecret (line 102) | public setServerSecret(secret: string) {
    method forceRecheck (line 106) | public async forceRecheck() {
    method isUnlocked (line 114) | public async isUnlocked(): Promise<boolean> {
    method check (line 124) | public async check(): Promise<LicenseStatus> {
    method activateLicenseKey (line 389) | public async activateLicenseKey(key: string) {
    method phoneHome (line 495) | private async phoneHome(

FILE: server/private/license/licenseJwt.ts
  function validateJWT (line 22) | function validateJWT<Payload>(token: string, publicKey: string): Payload {
  function verify (line 60) | function verify(
  function base64URLToBase64 (line 104) | function base64URLToBase64(base64url: string): string {

FILE: server/private/middlewares/logActionAudit.ts
  function getActionDays (line 24) | async function getActionDays(orgId: string): Promise<number> {
  function cleanUpOldLogs (line 53) | async function cleanUpOldLogs(orgId: string, retentionDays: number) {
  function logActionAudit (line 74) | function logActionAudit(action: ActionsEnum) {

FILE: server/private/middlewares/verifyCertificateAccess.ts
  function verifyCertificateAccess (line 23) | async function verifyCertificateAccess(

FILE: server/private/middlewares/verifyIdpAccess.ts
  function verifyIdpAccess (line 20) | async function verifyIdpAccess(

FILE: server/private/middlewares/verifyLoginPageAccess.ts
  function verifyLoginPageAccess (line 20) | async function verifyLoginPageAccess(

FILE: server/private/middlewares/verifyRemoteExitNodeAccess.ts
  function verifyRemoteExitNodeAccess (line 21) | async function verifyRemoteExitNodeAccess(

FILE: server/private/middlewares/verifySubscription.ts
  function verifyValidSubscription (line 21) | function verifyValidSubscription(tiers: Tier[]) {

FILE: server/private/middlewares/verifyValidLicense.ts
  function verifyValidLicense (line 20) | async function verifyValidLicense(

FILE: server/private/routers/approvals/countApprovals.ts
  type CountApprovalsResponse (line 37) | type CountApprovalsResponse = {
  function countApprovals (line 41) | async function countApprovals(

FILE: server/private/routers/approvals/listApprovals.ts
  function queryApprovals (line 72) | async function queryApprovals({
  type ListApprovalsResponse (line 227) | type ListApprovalsResponse = {
  function listApprovals (line 239) | async function listApprovals(

FILE: server/private/routers/approvals/processPendingApproval.ts
  type ProcessApprovalResponse (line 34) | type ProcessApprovalResponse = Approval;
  function processPendingApproval (line 36) | async function processPendingApproval(

FILE: server/private/routers/auditLogs/exportAccessAuditLog.ts
  function exportAccessAuditLogs (line 43) | async function exportAccessAuditLogs(

FILE: server/private/routers/auditLogs/exportActionAuditLog.ts
  function exportActionAuditLogs (line 43) | async function exportActionAuditLogs(

FILE: server/private/routers/auditLogs/queryAccessAuditLog.ts
  type Q (line 94) | type Q = z.infer<typeof queryAccessAuditLogsCombined>;
  function getWhere (line 96) | function getWhere(data: Q) {
  function queryAccess (line 117) | function queryAccess(data: Q) {
  function enrichWithResourceDetails (line 138) | async function enrichWithResourceDetails(logs: Awaited<ReturnType<typeof...
  function countAccessQuery (line 172) | function countAccessQuery(data: Q) {
  function queryUniqueFilterAttributes (line 180) | async function queryUniqueFilterAttributes(
  function queryAccessAuditLogs (line 260) | async function queryAccessAuditLogs(

FILE: server/private/routers/auditLogs/queryActionAuditLog.ts
  type Q (line 83) | type Q = z.infer<typeof queryActionAuditLogsCombined>;
  function getWhere (line 85) | function getWhere(data: Q) {
  function queryAction (line 99) | function queryAction(data: Q) {
  function countActionQuery (line 115) | function countActionQuery(data: Q) {
  function queryUniqueFilterAttributes (line 123) | async function queryUniqueFilterAttributes(
  function queryActionAuditLogs (line 171) | async function queryActionAuditLogs(

FILE: server/private/routers/auth/getSessionTransferToken.ts
  type GetSessionTransferTokenRenponse (line 33) | type GetSessionTransferTokenRenponse = {
  function getSessionTransferToken (line 37) | async function getSessionTransferToken(

FILE: server/private/routers/auth/transferSession.ts
  type TransferSessionBodySchema (line 35) | type TransferSessionBodySchema = z.infer<typeof bodySchema>;
  function transferSession (line 37) | async function transferSession(

FILE: server/private/routers/billing/changeTier.ts
  function changeTier (line 41) | async function changeTier(

FILE: server/private/routers/billing/createCheckoutSession.ts
  function createCheckoutSession (line 41) | async function createCheckoutSession(

FILE: server/private/routers/billing/createPortalSession.ts
  function createPortalSession (line 30) | async function createPortalSession(

FILE: server/private/routers/billing/featureLifecycle.ts
  function getMaxRetentionDaysForTier (line 37) | function getMaxRetentionDaysForTier(tier: Tier | null): number | null {
  function capRetentionDays (line 59) | async function capRetentionDays(
  function handleTierChange (line 132) | async function handleTierChange(
  function handleTierChangeForOrg (line 161) | async function handleTierChangeForOrg(
  function disableFeature (line 228) | async function disableFeature(
  function disableOrgOidc (line 312) | async function disableOrgOidc(orgId: string): Promise<void> {}
  function disableDeviceApprovals (line 314) | async function disableDeviceApprovals(orgId: string): Promise<void> {
  function disableSshPam (line 323) | async function disableSshPam(orgId: string): Promise<void> {
  function disableLoginPageBranding (line 329) | async function disableLoginPageBranding(orgId: string): Promise<void> {
  function disableLoginPageDomain (line 349) | async function disableLoginPageDomain(orgId: string): Promise<void> {
  function disableLogExport (line 375) | async function disableLogExport(orgId: string): Promise<void> {}
  function disableAccessLogs (line 377) | async function disableAccessLogs(orgId: string): Promise<void> {
  function disableActionLogs (line 386) | async function disableActionLogs(orgId: string): Promise<void> {
  function disableRotateCredentials (line 395) | async function disableRotateCredentials(orgId: string): Promise<void> {}
  function disableMaintencePage (line 397) | async function disableMaintencePage(orgId: string): Promise<void> {
  function disableDevicePosture (line 408) | async function disableDevicePosture(orgId: string): Promise<void> {}
  function disableTwoFactorEnforcement (line 410) | async function disableTwoFactorEnforcement(orgId: string): Promise<void> {
  function disableSessionDurationPolicies (line 419) | async function disableSessionDurationPolicies(orgId: string): Promise<vo...
  function disablePasswordExpirationPolicies (line 428) | async function disablePasswordExpirationPolicies(orgId: string): Promise...
  function disableAutoProvisioning (line 437) | async function disableAutoProvisioning(orgId: string): Promise<void> {

FILE: server/private/routers/billing/getOrgSubscriptions.ts
  function getOrgSubscriptions (line 42) | async function getOrgSubscriptions(
  function getOrgSubscriptionsData (line 102) | async function getOrgSubscriptionsData(

FILE: server/private/routers/billing/getOrgUsage.ts
  function getOrgUsage (line 45) | async function getOrgUsage(

FILE: server/private/routers/billing/hooks/getSubType.ts
  type SubscriptionType (line 25) | type SubscriptionType = Tier | "license";
  function getSubType (line 27) | function getSubType(fullSubscription: Stripe.Response<Stripe.Subscriptio...

FILE: server/private/routers/billing/hooks/handleCustomerCreated.ts
  function handleCustomerCreated (line 19) | async function handleCustomerCreated(

FILE: server/private/routers/billing/hooks/handleCustomerDeleted.ts
  function handleCustomerDeleted (line 19) | async function handleCustomerDeleted(

FILE: server/private/routers/billing/hooks/handleCustomerUpdated.ts
  function handleCustomerUpdated (line 19) | async function handleCustomerUpdated(

FILE: server/private/routers/billing/hooks/handleSubscriptionCreated.ts
  function handleSubscriptionCreated (line 36) | async function handleSubscriptionCreated(

FILE: server/private/routers/billing/hooks/handleSubscriptionDeleted.ts
  function handleSubscriptionDeleted (line 31) | async function handleSubscriptionDeleted(

FILE: server/private/routers/billing/hooks/handleSubscriptionUpdated.ts
  function handleSubscriptionUpdated (line 33) | async function handleSubscriptionUpdated(
  function calculateNextRollOverDate (line 336) | function calculateNextRollOverDate(lastRollover: Date, anchorDate: Date)...

FILE: server/private/routers/billing/internalGetOrgTier.ts
  function getOrgTier (line 28) | async function getOrgTier(

FILE: server/private/routers/billing/subscriptionLifecycle.ts
  function getLimitSetForSubscriptionType (line 25) | function getLimitSetForSubscriptionType(
  function handleSubscriptionLifesycle (line 44) | async function handleSubscriptionLifesycle(

FILE: server/private/routers/billing/webhooks.ts
  function billingWebhookHandler (line 29) | async function billingWebhookHandler(

FILE: server/private/routers/certificates/createCertificate.ts
  function createCertificate (line 25) | async function createCertificate(

FILE: server/private/routers/certificates/getCertificate.ts
  function query (line 32) | async function query(domainId: string, domain: string) {
  function getCertificate (line 113) | async function getCertificate(

FILE: server/private/routers/certificates/restartCertificate.ts
  function restartCertificate (line 46) | async function restartCertificate(

FILE: server/private/routers/domain/checkDomainNamespaceAvailability.ts
  function checkDomainNamespaceAvailability (line 44) | async function checkDomainNamespaceAvailability(

FILE: server/private/routers/domain/listDomainNamespaces.ts
  function query (line 43) | async function query(limit: number, offset: number) {
  type ListDomainNamespacesResponse (line 59) | type ListDomainNamespacesResponse = {
  function listDomainNamespaces (line 75) | async function listDomainNamespaces(

FILE: server/private/routers/generatedLicense/generateNewEnterpriseLicense.ts
  function generateNewEnterpriseLicense (line 34) | async function generateNewEnterpriseLicense(

FILE: server/private/routers/generatedLicense/generateNewLicense.ts
  type CreateNewLicenseResponse (line 22) | interface CreateNewLicenseResponse {
  type Data (line 30) | interface Data {
  type LicenseKey (line 34) | interface LicenseKey {
  function createNewLicense (line 52) | async function createNewLicense(orgId: string, licenseData: any): Promis...
  function generateNewLicense (line 77) | async function generateNewLicense(

FILE: server/private/routers/generatedLicense/listGeneratedLicenses.ts
  function fetchLicenseKeys (line 25) | async function fetchLicenseKeys(orgId: string): Promise<any> {
  function listSaasLicenseKeys (line 52) | async function listSaasLicenseKeys(

FILE: server/private/routers/gerbil/createExitNode.ts
  function createExitNode (line 21) | async function createExitNode(

FILE: server/private/routers/hybrid.ts
  type GetResourceByDomainParams (line 161) | type GetResourceByDomainParams = z.infer<
  type GetUserSessionParams (line 164) | type GetUserSessionParams = z.infer<typeof getUserSessionParamsSchema>;
  type GetUserOrgRoleParams (line 165) | type GetUserOrgRoleParams = z.infer<typeof getUserOrgRoleParamsSchema>;
  type GetRoleResourceAccessParams (line 166) | type GetRoleResourceAccessParams = z.infer<
  type GetUserResourceAccessParams (line 169) | type GetUserResourceAccessParams = z.infer<
  type GetResourceRulesParams (line 172) | type GetResourceRulesParams = z.infer<
  type ValidateResourceSessionTokenParams (line 175) | type ValidateResourceSessionTokenParams = z.infer<
  type ValidateResourceSessionTokenBody (line 178) | type ValidateResourceSessionTokenBody = z.infer<
  type ResourceWithAuth (line 183) | type ResourceWithAuth = {
  type UserSessionWithUser (line 192) | type UserSessionWithUser = {
  function loadEncryptData (line 275) | function loadEncryptData() {

FILE: server/private/routers/license/activateLicense.ts
  function activateLicense (line 27) | async function activateLicense(

FILE: server/private/routers/license/deleteLicenseKey.ts
  function deleteLicenseKey (line 30) | async function deleteLicenseKey(

FILE: server/private/routers/license/getLicenseStatus.ts
  function getLicenseStatus (line 22) | async function getLicenseStatus(

FILE: server/private/routers/license/listLicenseKeys.ts
  function listLicenseKeys (line 22) | async function listLicenseKeys(

FILE: server/private/routers/license/recheckStatus.ts
  function recheckStatus (line 21) | async function recheckStatus(

FILE: server/private/routers/loginPage/createLoginPage.ts
  type CreateLoginPageBody (line 45) | type CreateLoginPageBody = z.infer<typeof bodySchema>;
  function createLoginPage (line 47) | async function createLoginPage(

FILE: server/private/routers/loginPage/deleteLoginPage.ts
  function deleteLoginPage (line 32) | async function deleteLoginPage(

FILE: server/private/routers/loginPage/deleteLoginPageBranding.ts
  function deleteLoginPageBranding (line 36) | async function deleteLoginPageBranding(

FILE: server/private/routers/loginPage/getLoginPage.ts
  function query (line 29) | async function query(orgId: string) {
  function getLoginPage (line 42) | async function getLoginPage(

FILE: server/private/routers/loginPage/getLoginPageBranding.ts
  function getLoginPageBranding (line 34) | async function getLoginPageBranding(

FILE: server/private/routers/loginPage/loadLoginPage.ts
  function query (line 32) | async function query(orgId: string | undefined, fullDomain: string) {
  function loadLoginPage (line 84) | async function loadLoginPage(

FILE: server/private/routers/loginPage/loadLoginPageBranding.ts
  function query (line 29) | async function query(orgId: string) {
  function loadLoginPageBranding (line 63) | async function loadLoginPageBranding(

FILE: server/private/routers/loginPage/updateLoginPage.ts
  type UpdateLoginPageBody (line 56) | type UpdateLoginPageBody = z.infer<typeof bodySchema>;
  function updateLoginPage (line 58) | async function updateLoginPage(

FILE: server/private/routers/loginPage/upsertLoginPageBranding.ts
  type UpdateLoginPageBrandingBody (line 122) | type UpdateLoginPageBrandingBody = z.infer<typeof bodySchema>;
  function upsertLoginPageBranding (line 124) | async function upsertLoginPageBranding(

FILE: server/private/routers/misc/sendSupportEmail.ts
  function sendSupportEmail (line 30) | async function sendSupportEmail(

FILE: server/private/routers/org/sendUsageNotifications.ts
  type SendUsageNotificationRequest (line 41) | type SendUsageNotificationRequest = z.infer<
  type SendUsageNotificationResponse (line 45) | type SendUsageNotificationResponse = {
  function getOrgAdmins (line 83) | async function getOrgAdmins(orgId: string) {
  function sendUsageNotification (line 114) | async function sendUsageNotification(

FILE: server/private/routers/orgIdp/createOrgOidcIdp.ts
  function createOrgOidcIdp (line 69) | async function createOrgOidcIdp(

FILE: server/private/routers/orgIdp/deleteOrgIdp.ts
  function deleteOrgIdp (line 45) | async function deleteOrgIdp(

FILE: server/private/routers/orgIdp/getOrgIdp.ts
  function query (line 37) | async function query(idpId: number, orgId: string) {
  function getOrgIdp (line 62) | async function getOrgIdp(

FILE: server/private/routers/orgIdp/listOrgIdps.ts
  function query (line 46) | async function query(orgId: string, limit: number, offset: number) {
  function listOrgIdps (line 78) | async function listOrgIdps(

FILE: server/private/routers/orgIdp/updateOrgOidcIdp.ts
  type UpdateOrgIdpResponse (line 54) | type UpdateOrgIdpResponse = {
  function updateOrgOidcIdp (line 76) | async function updateOrgOidcIdp(

FILE: server/private/routers/re-key/reGenerateClientSecret.ts
  type ReGenerateSecretBody (line 40) | type ReGenerateSecretBody = z.infer<typeof reGenerateSecretBodySchema>;
  function reGenerateClientSecret (line 42) | async function reGenerateClientSecret(

FILE: server/private/routers/re-key/reGenerateExitNodeSecret.ts
  function reGenerateExitNodeSecret (line 45) | async function reGenerateExitNodeSecret(

FILE: server/private/routers/re-key/reGenerateSiteSecret.ts
  function reGenerateSiteSecret (line 40) | async function reGenerateSiteSecret(

FILE: server/private/routers/remoteExitNode/createRemoteExitNode.ts
  type CreateRemoteExitNodeBody (line 50) | type CreateRemoteExitNodeBody = z.infer<typeof bodySchema>;
  function createRemoteExitNode (line 52) | async function createRemoteExitNode(

FILE: server/private/routers/remoteExitNode/deleteRemoteExitNode.ts
  function deleteRemoteExitNode (line 32) | async function deleteRemoteExitNode(

FILE: server/private/routers/remoteExitNode/getRemoteExitNode.ts
  function query (line 31) | async function query(remoteExitNodeId: string) {
  function getRemoteExitNode (line 54) | async function getRemoteExitNode(

FILE: server/private/routers/remoteExitNode/getRemoteExitNodeToken.ts
  function getRemoteExitNodeToken (line 38) | async function getRemoteExitNodeToken(

FILE: server/private/routers/remoteExitNode/handleRemoteExitNodePingMessage.ts
  constant OFFLINE_CHECK_INTERVAL (line 22) | const OFFLINE_CHECK_INTERVAL = 30 * 1000;
  constant OFFLINE_THRESHOLD_MS (line 23) | const OFFLINE_THRESHOLD_MS = 2 * 60 * 1000;

FILE: server/private/routers/remoteExitNode/listRemoteExitNodes.ts
  function queryRemoteExitNodes (line 45) | function queryRemoteExitNodes(orgId: string) {
  function listRemoteExitNodes (line 67) | async function listRemoteExitNodes(

FILE: server/private/routers/remoteExitNode/pickRemoteExitNodeDefaults.ts
  function pickRemoteExitNodeDefaults (line 28) | async function pickRemoteExitNodeDefaults(

FILE: server/private/routers/remoteExitNode/quickStartRemoteExitNode.ts
  constant INSTALLER_KEY (line 29) | const INSTALLER_KEY = "af4e4785-7e09-11f0-b93a-74563c4e2a7e";
  function quickStartRemoteExitNode (line 35) | async function quickStartRemoteExitNode(

FILE: server/private/routers/resource/getMaintenanceInfo.ts
  function query (line 33) | async function query(fullDomain: string) {
  function getMaintenanceInfo (line 71) | async function getMaintenanceInfo(

FILE: server/private/routers/ssh/signSshKey.ts
  type SignSshKeyResponse (line 63) | type SignSshKeyResponse = {
  function signSshKey (line 95) | async function signSshKey(

FILE: server/private/routers/ws/ws.ts
  constant MAX_PENDING_MESSAGES (line 56) | const MAX_PENDING_MESSAGES = 50;
  constant NODE_ID (line 191) | const NODE_ID = uuidv4();
  constant REDIS_CHANNEL (line 192) | const REDIS_CHANNEL = "websocket_messages";
  constant PING_DB_WRITE_INTERVAL (line 204) | const PING_DB_WRITE_INTERVAL = 45;

FILE: server/routers/accessToken/deleteAccessToken.ts
  function deleteAccessToken (line 28) | async function deleteAccessToken(

FILE: server/routers/accessToken/generateAccessToken.ts
  type GenerateAccessTokenResponse (line 37) | type GenerateAccessTokenResponse = Omit<
  function generateAccessToken (line 60) | async function generateAccessToken(

FILE: server/routers/accessToken/listAccessTokens.ts
  function queryAccessTokens (line 49) | function queryAccessTokens(
  type ListAccessTokensResponse (line 116) | type ListAccessTokensResponse = {
  function listAccessTokens (line 149) | async function listAccessTokens(

FILE: server/routers/apiKeys/createOrgApiKey.ts
  type CreateOrgApiKeyBody (line 26) | type CreateOrgApiKeyBody = z.infer<typeof bodySchema>;
  type CreateOrgApiKeyResponse (line 28) | type CreateOrgApiKeyResponse = {
  function createOrgApiKey (line 54) | async function createOrgApiKey(

FILE: server/routers/apiKeys/createRootApiKey.ts
  type CreateRootApiKeyBody (line 21) | type CreateRootApiKeyBody = z.infer<typeof bodySchema>;
  type CreateRootApiKeyResponse (line 23) | type CreateRootApiKeyResponse = {
  function createRootApiKey (line 31) | async function createRootApiKey(

FILE: server/routers/apiKeys/deleteApiKey.ts
  function deleteApiKey (line 28) | async function deleteApiKey(

FILE: server/routers/apiKeys/deleteOrgApiKey.ts
  function deleteOrgApiKey (line 17) | async function deleteOrgApiKey(

FILE: server/routers/apiKeys/getApiKey.ts
  function query (line 16) | async function query(apiKeyId: string) {
  type GetApiKeyResponse (line 30) | type GetApiKeyResponse = NonNullable<
  function getApiKey (line 34) | async function getApiKey(

FILE: server/routers/apiKeys/listApiKeyActions.ts
  function queryActions (line 32) | function queryActions(apiKeyId: string) {
  type ListApiKeyActionsResponse (line 42) | type ListApiKeyActionsResponse = {
  function listApiKeyActions (line 59) | async function listApiKeyActions(

FILE: server/routers/apiKeys/listOrgApiKeys.ts
  function queryApiKeys (line 32) | function queryApiKeys(orgId: string) {
  type ListOrgApiKeysResponse (line 46) | type ListOrgApiKeysResponse = {
  function listOrgApiKeys (line 63) | async function listOrgApiKeys(

FILE: server/routers/apiKeys/listRootApiKeys.ts
  function queryApiKeys (line 27) | function queryApiKeys() {
  type ListRootApiKeysResponse (line 39) | type ListRootApiKeysResponse = {
  function listRootApiKeys (line 44) | async function listRootApiKeys(

FILE: server/routers/apiKeys/setApiKeyActions.ts
  function setApiKeyActions (line 42) | async function setApiKeyActions(

FILE: server/routers/apiKeys/setApiKeyOrgs.ts
  function setApiKeyOrgs (line 22) | async function setApiKeyOrgs(

FILE: server/routers/auditLogs/exportRequestAuditLog.ts
  constant MAX_EXPORT_LIMIT (line 17) | const MAX_EXPORT_LIMIT = 50_000;
  function exportRequestAuditLogs (line 34) | async function exportRequestAuditLogs(

FILE: server/routers/auditLogs/generateCSV.ts
  function generateCSV (line 1) | function generateCSV(data: any[]): string {

FILE: server/routers/auditLogs/queryRequestAnalytics.ts
  type Q (line 61) | type Q = z.infer<typeof queryRequestAuditLogsCombined>;
  function query (line 63) | async function query(query: Q) {
  type QueryRequestAnalyticsResponse (line 162) | type QueryRequestAnalyticsResponse = Awaited<ReturnType<typeof query>>;
  function queryRequestAnalytics (line 164) | async function queryRequestAnalytics(

FILE: server/routers/auditLogs/queryRequestAuditLog.ts
  type Q (line 87) | type Q = z.infer<typeof queryRequestAuditLogsCombined>;
  function getWhere (line 89) | function getWhere(data: Q) {
  function queryRequest (line 109) | function queryRequest(data: Q) {
  function enrichWithResourceDetails (line 139) | async function enrichWithResourceDetails(logs: Awaited<ReturnType<typeof...
  function countRequestQuery (line 173) | function countRequestQuery(data: Q) {
  function queryUniqueFilterAttributes (line 193) | async function queryUniqueFilterAttributes(
  function queryRequestAuditLogs (line 296) | async function queryRequestAuditLogs(

FILE: server/routers/auditLogs/types.ts
  type QueryActionAuditLogResponse (line 1) | type QueryActionAuditLogResponse = {
  type QueryRequestAuditLogResponse (line 21) | type QueryRequestAuditLogResponse = {
  type QueryAccessAuditLogResponse (line 63) | type QueryAccessAuditLogResponse = {

FILE: server/routers/auth/changePassword.ts
  type ChangePasswordBody (line 28) | type ChangePasswordBody = z.infer<typeof changePasswordBody>;
  type ChangePasswordResponse (line 30) | type ChangePasswordResponse = {
  function invalidateAllSessionsExceptCurrent (line 34) | async function invalidateAllSessionsExceptCurrent(
  function changePassword (line 76) | async function changePassword(

FILE: server/routers/auth/checkResourceSession.ts
  type CheckResourceSessionParams (line 15) | type CheckResourceSessionParams = z.infer<typeof params>;
  type CheckResourceSessionResponse (line 17) | type CheckResourceSessionResponse = {
  function checkResourceSession (line 21) | async function checkResourceSession(

FILE: server/routers/auth/deleteMyAccount.ts
  type DeleteMyAccountPreviewResponse (line 31) | type DeleteMyAccountPreviewResponse = {
  type DeleteMyAccountCodeRequestedResponse (line 37) | type DeleteMyAccountCodeRequestedResponse = {
  type DeleteMyAccountSuccessResponse (line 41) | type DeleteMyAccountSuccessResponse = {
  function deleteMyAccount (line 45) | async function deleteMyAccount(

FILE: server/routers/auth/disable2fa.ts
  type Disable2faBody (line 24) | type Disable2faBody = z.infer<typeof disable2faBody>;
  type Disable2faResponse (line 26) | type Disable2faResponse = {
  function disable2fa (line 30) | async function disable2fa(

FILE: server/routers/auth/initialSetupComplete.ts
  type InitialSetupCompleteResponse (line 9) | type InitialSetupCompleteResponse = {
  function initialSetupComplete (line 13) | async function initialSetupComplete(

FILE: server/routers/auth/login.ts
  type LoginBody (line 32) | type LoginBody = z.infer<typeof loginBodySchema>;
  type LoginResponse (line 34) | type LoginResponse = {
  function login (line 41) | async function login(

FILE: server/routers/auth/logout.ts
  function logout (line 13) | async function logout(

FILE: server/routers/auth/lookupUser.ts
  type LookupUserResponse (line 25) | type LookupUserResponse = {
  function lookupUser (line 57) | async function lookupUser(

FILE: server/routers/auth/pollDeviceWebAuth.ts
  type PollDeviceWebAuthParams (line 19) | type PollDeviceWebAuthParams = z.infer<typeof paramsSchema>;
  function hashDeviceCode (line 22) | function hashDeviceCode(code: string): string {
  type PollDeviceWebAuthResponse (line 26) | type PollDeviceWebAuthResponse = {
  function pollDeviceWebAuth (line 31) | async function pollDeviceWebAuth(

FILE: server/routers/auth/requestEmailVerificationCode.ts
  type RequestEmailVerificationCodeResponse (line 11) | type RequestEmailVerificationCodeResponse = {
  function requestEmailVerificationCode (line 15) | async function requestEmailVerificationCode(

FILE: server/routers/auth/requestPasswordReset.ts
  type RequestPasswordResetBody (line 24) | type RequestPasswordResetBody = z.infer<typeof requestPasswordResetBody>;
  type RequestPasswordResetResponse (line 26) | type RequestPasswordResetResponse = {
  function requestPasswordReset (line 30) | async function requestPasswordReset(
  function randomDelay (line 143) | async function randomDelay(maxDelayMs: number) {

FILE: server/routers/auth/requestTotpSecret.ts
  type RequestTotpSecretBody (line 24) | type RequestTotpSecretBody = z.infer<typeof requestTotpSecretBody>;
  type RequestTotpSecretResponse (line 26) | type RequestTotpSecretResponse = {
  function requestTotpSecret (line 31) | async function requestTotpSecret(

FILE: server/routers/auth/resetPassword.ts
  type ResetPasswordBody (line 27) | type ResetPasswordBody = z.infer<typeof resetPasswordBody>;
  type ResetPasswordResponse (line 29) | type ResetPasswordResponse = {
  function resetPassword (line 33) | async function resetPassword(

FILE: server/routers/auth/securityKey.ts
  function storeChallenge (line 68) | async function storeChallenge(
  function getChallenge (line 91) | async function getChallenge(sessionId: string) {
  function clearChallenge (line 113) | async function clearChallenge(sessionId: string) {
  function startRegistration (line 142) | async function startRegistration(
  function verifyRegistration (line 268) | async function verifyRegistration(
  function listSecurityKeys (line 363) | async function listSecurityKeys(
  function deleteSecurityKey (line 404) | async function deleteSecurityKey(
  function startAuthentication (line 506) | async function startAuthentication(
  function verifyAuthentication (line 608) | async function verifyAuthentication(

FILE: server/routers/auth/setServerAdmin.ts
  type SetServerAdminBody (line 22) | type SetServerAdminBody = z.infer<typeof bodySchema>;
  type SetServerAdminResponse (line 24) | type SetServerAdminResponse = null;
  function setServerAdmin (line 26) | async function setServerAdmin(

FILE: server/routers/auth/signup.ts
  type SignUpBody (line 36) | type SignUpBody = z.infer<typeof signupBodySchema>;
  type SignUpResponse (line 38) | type SignUpResponse = {
  function signup (line 42) | async function signup(

FILE: server/routers/auth/startDeviceWebAuth.ts
  type StartDeviceWebAuthBody (line 24) | type StartDeviceWebAuthBody = z.infer<typeof bodySchema>;
  type StartDeviceWebAuthResponse (line 26) | type StartDeviceWebAuthResponse = {
  function generateDeviceCode (line 32) | function generateDeviceCode(): string {
  function hashDeviceCode (line 39) | function hashDeviceCode(code: string): string {
  function getCityFromIp (line 44) | async function getCityFromIp(ip: string): Promise<string | undefined> {
  function startDeviceWebAuth (line 66) | async function startDeviceWebAuth(

FILE: server/routers/auth/types.ts
  type TransferSessionResponse (line 1) | type TransferSessionResponse = {
  type GetSessionTransferTokenRenponse (line 6) | type GetSessionTransferTokenRenponse = {

FILE: server/routers/auth/validateSetupToken.ts
  type ValidateSetupTokenResponse (line 15) | type ValidateSetupTokenResponse = {
  function validateSetupToken (line 20) | async function validateSetupToken(

FILE: server/routers/auth/verifyDeviceWebAuth.ts
  function hashDeviceCode (line 23) | function hashDeviceCode(code: string): string {
  type VerifyDeviceWebAuthBody (line 27) | type VerifyDeviceWebAuthBody = z.infer<typeof bodySchema>;
  type VerifyDeviceWebAuthResponse (line 29) | type VerifyDeviceWebAuthResponse = {
  function verifyDeviceWebAuth (line 41) | async function verifyDeviceWebAuth(

FILE: server/routers/auth/verifyEmail.ts
  type VerifyEmailBody (line 20) | type VerifyEmailBody = z.infer<typeof verifyEmailBody>;
  type VerifyEmailResponse (line 22) | type VerifyEmailResponse = {
  function verifyEmail (line 26) | async function verifyEmail(
  function isValidCode (line 126) | async function isValidCode(user: User, code: string): Promise<boolean> {

FILE: server/routers/auth/verifyTotp.ts
  type VerifyTotpBody (line 27) | type VerifyTotpBody = z.infer<typeof verifyTotpBody>;
  type VerifyTotpResponse (line 29) | type VerifyTotpResponse = {
  function verifyTotp (line 34) | async function verifyTotp(

FILE: server/routers/badger/exchangeSession.ts
  type ExchangeSessionBodySchema (line 30) | type ExchangeSessionBodySchema = z.infer<
  type ExchangeSessionResponse (line 34) | type ExchangeSessionResponse = {
  function exchangeSession (line 39) | async function exchangeSession(

FILE: server/routers/badger/logRequestAudit.ts
  constant BATCH_SIZE (line 50) | const BATCH_SIZE = 100;
  constant BATCH_INTERVAL_MS (line 51) | const BATCH_INTERVAL_MS = 5000;
  constant MAX_BUFFER_SIZE (line 52) | const MAX_BUFFER_SIZE = 10000;
  function flushAuditLogs (line 59) | async function flushAuditLogs() {
  function scheduleFlush (line 105) | function scheduleFlush() {
  function shutdownAuditLogger (line 119) | async function shutdownAuditLogger() {
  function getRetentionDays (line 131) | async function getRetentionDays(orgId: string): Promise<number> {
  function cleanUpOldLogs (line 161) | async function cleanUpOldLogs(orgId: string, retentionDays: number) {
  function logRequestAudit (line 182) | async function logRequestAudit(

FILE: server/routers/badger/verifySession.test.ts
  function isPathAllowed (line 3) | function isPathAllowed(pattern: string, path: string): boolean {
  function runTests (line 73) | function runTests() {

FILE: server/routers/badger/verifySession.ts
  type VerifyResourceSessionSchema (line 59) | type VerifyResourceSessionSchema = z.infer<
  type BasicUserData (line 63) | type BasicUserData = {
  type VerifyUserResponse (line 70) | type VerifyUserResponse = {
  function verifyResourceSession (line 78) | async function verifyResourceSession(
  function extractResourceSessionToken (line 750) | function extractResourceSessionToken(
  function notAllowed (line 793) | async function notAllowed(
  function allowed (line 836) | function allowed(res: Response, userData?: BasicUserData) {
  function headerAuthChallenged (line 850) | async function headerAuthChallenged(
  function isUserAllowedToAccessResource (line 895) | async function isUserAllowedToAccessResource(
  function checkRules (line 968) | async function checkRules(
  function isPathAllowed (line 1029) | function isPathAllowed(pattern: string, path: string): boolean {
  function isIpInGeoIP (line 1167) | async function isIpInGeoIP(
  function isIpInAsn (line 1178) | async function isIpInAsn(
  function getAsnFromIp (line 1208) | async function getAsnFromIp(ip: string): Promise<number | undefined> {
  function getCountryCodeFromIp (line 1224) | async function getCountryCodeFromIp(ip: string): Promise<string | undefi...
  function extractBasicAuth (line 1241) | function extractBasicAuth(

FILE: server/routers/billing/types.ts
  type GetOrgSubscriptionResponse (line 3) | type GetOrgSubscriptionResponse = {
  type GetOrgUsageResponse (line 9) | type GetOrgUsageResponse = {
  type GetOrgTierResponse (line 14) | type GetOrgTierResponse = {

FILE: server/routers/billing/webhooks.ts
  function billingWebhookHandler (line 5) | async function billingWebhookHandler(

FILE: server/routers/blueprints/applyJSONBlueprint.ts
  function applyJSONBlueprint (line 37) | async function applyJSONBlueprint(

FILE: server/routers/blueprints/applyYAMLBlueprint.ts
  type CreateBlueprintResponse (line 40) | type CreateBlueprintResponse = Blueprint;
  function applyYAMLBlueprint (line 60) | async function applyYAMLBlueprint(

FILE: server/routers/blueprints/getBlueprint.ts
  function query (line 20) | async function query(blueprintId: number, orgId: string) {
  type GetBlueprintResponse (line 50) | type GetBlueprintResponse = BlueprintData;
  function getBlueprint (line 63) | async function getBlueprint(

FILE: server/routers/blueprints/listBlueprints.ts
  function queryBlueprints (line 32) | async function queryBlueprints(orgId: string, limit: number, offset: num...
  type ListBlueprintsResponse (line 51) | type ListBlueprintsResponse = {
  function listBlueprints (line 80) | async function listBlueprints(

FILE: server/routers/blueprints/types.ts
  type BlueprintSource (line 3) | type BlueprintSource = "API" | "UI" | "NEWT" | "CLI";
  type BlueprintData (line 5) | type BlueprintData = Omit<Blueprint, "source"> & {

FILE: server/routers/certificates/createCertificate.ts
  function createCertificate (line 3) | async function createCertificate(

FILE: server/routers/certificates/types.ts
  type GetCertificateResponse (line 1) | type GetCertificateResponse = {

FILE: server/routers/client/archiveClient.ts
  function archiveClient (line 28) | async function archiveClient(

FILE: server/routers/client/blockClient.ts
  function blockClient (line 30) | async function blockClient(

FILE: server/routers/client/createClient.ts
  type CreateClientBody (line 43) | type CreateClientBody = z.infer<typeof createClientSchema>;
  type CreateClientResponse (line 45) | type CreateClientResponse = Client;
  function createClient (line 65) | async function createClient(

FILE: server/routers/client/createUserClient.ts
  type CreateClientAndOlmBody (line 43) | type CreateClientAndOlmBody = z.infer<typeof bodySchema>;
  type CreateClientAndOlmResponse (line 45) | type CreateClientAndOlmResponse = Client;
  function createUserClient (line 66) | async function createUserClient(

FILE: server/routers/client/deleteClient.ts
  function deleteClient (line 31) | async function deleteClient(

FILE: server/routers/client/getClient.ts
  function query (line 29) | async function query(clientId?: number, niceId?: string, orgId?: string) {
  type PostureData (line 59) | type PostureData = {
  function maskPostureDataWithPlaceholder (line 73) | function maskPostureDataWithPlaceholder(posture: PostureData): PostureDa...
  function getPlatformPostureData (line 83) | function getPlatformPostureData(
  type GetClientResponse (line 217) | type GetClientResponse = NonNullable<
  function getClient (line 269) | async function getClient(

FILE: server/routers/client/listClients.ts
  function getLatestOlmVersion (line 39) | async function getLatestOlmVersion(): Promise<string | null> {
  function queryClientsBase (line 175) | function queryClientsBase() {
  function getSiteAssociations (line 206) | async function getSiteAssociations(clientIds: number[]) {
  type ClientWithSites (line 221) | type ClientWithSites = Awaited<ReturnType<typeof queryClientsBase>>[0] & {
  type OlmWithUpdateAvailable (line 230) | type OlmWithUpdateAvailable = ClientWithSites;
  type ListClientsResponse (line 232) | type ListClientsResponse = PaginatedResponse<{
  function listClients (line 248) | async function listClients(

FILE: server/routers/client/listUserDevices.ts
  function getLatestOlmVersion (line 40) | async function getLatestOlmVersion(): Promise<string | null> {
  function queryUserDevicesBase (line 206) | function queryUserDevicesBase() {
  type OlmWithUpdateAvailable (line 245) | type OlmWithUpdateAvailable = Awaited<
  type ListUserDevicesResponse (line 251) | type ListUserDevicesResponse = PaginatedResponse<{
  function listUserDevices (line 267) | async function listUserDevices(

FILE: server/routers/client/pickClientDefaults.ts
  type PickClientDefaultsResponse (line 12) | type PickClientDefaultsResponse = {
  function pickClientDefaults (line 33) | async function pickClientDefaults(

FILE: server/routers/client/targets.ts
  function addTargets (line 8) | async function addTargets(
  function removeTargets (line 23) | async function removeTargets(
  function updateTargets (line 38) | async function updateTargets(
  function addPeerData (line 61) | async function addPeerData(
  function removePeerData (line 98) | async function removePeerData(
  function updatePeerData (line 135) | async function updatePeerData(

FILE: server/routers/client/terminate.ts
  function sendTerminateClient (line 6) | async function sendTerminateClient(

FILE: server/routers/client/unarchiveClient.ts
  function unarchiveClient (line 28) | async function unarchiveClient(

FILE: server/routers/client/unblockClient.ts
  function unblockClient (line 28) | async function unblockClient(

FILE: server/routers/client/updateClient.ts
  type UpdateClientBody (line 22) | type UpdateClientBody = z.infer<typeof updateClientSchema>;
  function updateClient (line 42) | async function updateClient(

FILE: server/routers/domain/createOrgDomain.ts
  type CreateDomainResponse (line 36) | type CreateDomainResponse = {
  function isSubdomainOrEqual (line 47) | function isSubdomainOrEqual(a: string, b: string): boolean {
  function createOrgDomain (line 54) | async function createOrgDomain(

FILE: server/routers/domain/deleteOrgDomain.ts
  type DeleteAccountDomainResponse (line 18) | type DeleteAccountDomainResponse = {
  function deleteAccountDomain (line 22) | async function deleteAccountDomain(

FILE: server/routers/domain/getDNSRecords.ts
  function query (line 18) | async function query(domainId: string) {
  type GetDNSRecordsResponse (line 27) | type GetDNSRecordsResponse = Awaited<ReturnType<typeof query>>;
  function getDNSRecords (line 43) | async function getDNSRecords(

FILE: server/routers/domain/getDomain.ts
  function query (line 18) | async function query(domainId?: string, orgId?: string) {
  type GetDomainResponse (line 29) | type GetDomainResponse = NonNullable<Awaited<ReturnType<typeof query>>>;
  function getDomain (line 45) | async function getDomain(

FILE: server/routers/domain/listDomains.ts
  function queryDomains (line 32) | async function queryDomains(orgId: string, limit: number, offset: number) {
  type ListDomainsResponse (line 54) | type ListDomainsResponse = {
  function listDomains (line 73) | async function listDomains(

FILE: server/routers/domain/restartOrgDomain.ts
  type RestartOrgDomainResponse (line 16) | type RestartOrgDomainResponse = {
  function restartOrgDomain (line 20) | async function restartOrgDomain(

FILE: server/routers/domain/types.ts
  type CheckDomainAvailabilityResponse (line 1) | type CheckDomainAvailabilityResponse = {

FILE: server/routers/domain/updateDomain.ts
  type UpdateDomainResponse (line 22) | type UpdateDomainResponse = {
  function updateOrgDomain (line 42) | async function updateOrgDomain(

FILE: server/routers/generatedLicense/types.ts
  type GeneratedLicenseKey (line 1) | type GeneratedLicenseKey = {
  type ListGeneratedLicenseKeysResponse (line 13) | type ListGeneratedLicenseKeysResponse = GeneratedLicenseKey[];
  type NewLicenseKey (line 15) | type NewLicenseKey = {
  type GenerateNewLicenseResponse (line 33) | type GenerateNewLicenseResponse = NewLicenseKey;

FILE: server/routers/gerbil/createExitNode.ts
  function createExitNode (line 8) | async function createExitNode(

FILE: server/routers/gerbil/getAllRelays.ts
  type PeerDestination (line 26) | interface PeerDestination {
  type ProxyMapping (line 32) | interface ProxyMapping {
  function getAllRelays (line 36) | async function getAllRelays(
  function generateRelayMappings (line 89) | async function generateRelayMappings(exitNode: ExitNode) {

FILE: server/routers/gerbil/getConfig.ts
  type GetConfigResponse (line 20) | type GetConfigResponse = {
  function getConfig (line 29) | async function getConfig(
  function generateGerbilConfig (line 84) | async function generateGerbilConfig(exitNode: ExitNode) {

FILE: server/routers/gerbil/getResolvedHostname.ts
  function getResolvedHostname (line 17) | async function getResolvedHostname(

FILE: server/routers/gerbil/peers.ts
  function addPeer (line 7) | async function addPeer(
  function deletePeer (line 34) | async function deletePeer(exitNodeId: number, publicKey: string) {

FILE: server/routers/gerbil/receiveBandwidth.ts
  type PeerBandwidth (line 14) | interface PeerBandwidth {
  type AccumulatorEntry (line 20) | interface AccumulatorEntry {
  constant MAX_RETRIES (line 30) | const MAX_RETRIES = 3;
  constant BASE_DELAY_MS (line 31) | const BASE_DELAY_MS = 50;
  constant FLUSH_INTERVAL_MS (line 34) | const FLUSH_INTERVAL_MS = 30_000;
  function isDeadlockError (line 42) | function isDeadlockError(error: any): boolean {
  function withDeadlockRetry (line 53) | async function withDeadlockRetry<T>(
  function flushSiteBandwidthToDb (line 89) | async function flushSiteBandwidthToDb(): Promise<void> {
  function updateSiteBandwidth (line 252) | async function updateSiteBandwidth(

FILE: server/routers/gerbil/updateHolePunch.ts
  type PeerDestination (line 40) | interface PeerDestination {
  function updateHolePunch (line 45) | async function updateHolePunch(
  function updateAndGenerateEndpointDestinations (line 125) | async function updateAndGenerateEndpointDestinations(
  function handleSiteEndpointChange (line 396) | async function handleSiteEndpointChange(siteId: number, newEndpoint: str...
  function handleClientEndpointChange (line 459) | async function handleClientEndpointChange(

FILE: server/routers/idp/createIdpOrgPolicy.ts
  type CreateIdpOrgPolicyResponse (line 24) | type CreateIdpOrgPolicyResponse = {};
  function createIdpOrgPolicy (line 44) | async function createIdpOrgPolicy(

FILE: server/routers/idp/createOidcIdp.ts
  type CreateIdpResponse (line 31) | type CreateIdpResponse = {
  function createOidcIdp (line 53) | async function createOidcIdp(

FILE: server/routers/idp/deleteIdp.ts
  function deleteIdp (line 31) | async function deleteIdp(

FILE: server/routers/idp/deleteIdpOrgPolicy.ts
  function deleteIdpOrgPolicy (line 29) | async function deleteIdpOrgPolicy(

FILE: server/routers/idp/generateOidcUrl.ts
  type GenerateOidcUrlResponse (line 38) | type GenerateOidcUrlResponse = {
  function generateOidcUrl (line 42) | async function generateOidcUrl(

FILE: server/routers/idp/getIdp.ts
  function query (line 21) | async function query(idpId: number) {
  type GetIdpResponse (line 31) | type GetIdpResponse = NonNullable<Awaited<ReturnType<typeof query>>>;
  function getIdp (line 44) | async function getIdp(

FILE: server/routers/idp/listIdpOrgPolicies.ts
  function query (line 32) | async function query(idpId: number, limit: number, offset: number) {
  type ListIdpOrgPoliciesResponse (line 42) | type ListIdpOrgPoliciesResponse = {
  function listIdpOrgPolicies (line 59) | async function listIdpOrgPolicies(

FILE: server/routers/idp/listIdps.ts
  function query (line 28) | async function query(limit: number, offset: number) {
  type ListIdpsResponse (line 48) | type ListIdpsResponse = {
  function listIdps (line 68) | async function listIdps(

FILE: server/routers/idp/updateIdpOrgPolicy.ts
  type UpdateIdpOrgPolicyResponse (line 23) | type UpdateIdpOrgPolicyResponse = {};
  function updateIdpOrgPolicy (line 43) | async function updateIdpOrgPolicy(

FILE: server/routers/idp/updateOidcIdp.ts
  type UpdateIdpResponse (line 37) | type UpdateIdpResponse = {
  function updateOidcIdp (line 59) | async function updateOidcIdp(

FILE: server/routers/idp/validateOidcCallback.ts
  type ValidateOidcUrlCallbackResponse (line 64) | type ValidateOidcUrlCallbackResponse = {
  function validateOidcCallback (line 68) | async function validateOidcCallback(
  function hydrateOrgMapping (line 742) | function hydrateOrgMapping(

FILE: server/routers/license/types.ts
  type ActivateLicenseStatus (line 3) | type ActivateLicenseStatus = LicenseStatus;
  type DeleteLicenseKeyResponse (line 5) | type DeleteLicenseKeyResponse = LicenseStatus;
  type GetLicenseStatusResponse (line 7) | type GetLicenseStatusResponse = LicenseStatus;
  type ListLicenseKeysResponse (line 9) | type ListLicenseKeysResponse = LicenseKeyCache[];
  type RecheckStatusResponse (line 11) | type RecheckStatusResponse = LicenseStatus;

FILE: server/routers/loginPage/types.ts
  type CreateLoginPageResponse (line 3) | type CreateLoginPageResponse = LoginPage;
  type DeleteLoginPageResponse (line 5) | type DeleteLoginPageResponse = LoginPage;
  type GetLoginPageResponse (line 7) | type GetLoginPageResponse = LoginPage;
  type UpdateLoginPageResponse (line 9) | type UpdateLoginPageResponse = LoginPage;
  type LoadLoginPageResponse (line 11) | type LoadLoginPageResponse = LoginPage & { orgId: string };
  type LoadLoginPageBrandingResponse (line 13) | type LoadLoginPageBrandingResponse = LoginPageBranding & {
  type GetLoginPageBrandingResponse (line 18) | type GetLoginPageBrandingResponse = LoginPageBranding;

FILE: server/routers/newt/buildConfiguration.ts
  function buildClientConfigurationForNewtClient (line 19) | async function buildClientConfigurationForNewtClient(
  function buildTargetConfigurationForNewtClient (line 181) | async function buildTargetConfigurationForNewtClient(siteId: number) {

FILE: server/routers/newt/createNewt.ts
  type CreateNewtBody (line 18) | type CreateNewtBody = z.infer<typeof createNewtBodySchema>;
  type CreateNewtResponse (line 20) | type CreateNewtResponse = {
  function createNewt (line 31) | async function createNewt(

FILE: server/routers/newt/dockerSocket.ts
  function fetchContainers (line 3) | function fetchContainers(newtId: string) {
  function dockerSocket (line 11) | function dockerSocket(newtId: string) {

FILE: server/routers/newt/getNewtToken.ts
  type NewtGetTokenBody (line 26) | type NewtGetTokenBody = z.infer<typeof newtGetTokenBodySchema>;
  function getNewtToken (line 28) | async function getNewtToken(

FILE: server/routers/newt/handleGetConfigMessage.ts
  type Input (line 16) | type Input = z.infer<typeof inputSchema>;

FILE: server/routers/newt/handleNewtPingMessage.ts
  constant OFFLINE_CHECK_INTERVAL (line 11) | const OFFLINE_CHECK_INTERVAL = 30 * 1000;
  constant OFFLINE_THRESHOLD_MS (line 12) | const OFFLINE_THRESHOLD_MS = 2 * 60 * 1000;

FILE: server/routers/newt/handleNewtRegisterMessage.ts
  type ExitNodePingResult (line 18) | type ExitNodePingResult = {
  function getUniqueSubnetForSite (line 225) | async function getUniqueSubnetForSite(

FILE: server/routers/newt/handleReceiveBandwidthMessage.ts
  type PeerBandwidth (line 7) | interface PeerBandwidth {
  type BandwidthAccumulator (line 13) | interface BandwidthAccumulator {
  constant MAX_RETRIES (line 19) | const MAX_RETRIES = 3;
  constant BASE_DELAY_MS (line 20) | const BASE_DELAY_MS = 50;
  constant FLUSH_INTERVAL_MS (line 23) | const FLUSH_INTERVAL_MS = 120_000;
  function isDeadlockError (line 31) | function isDeadlockError(error: any): boolean {
  function withDeadlockRetry (line 42) | async function withDeadlockRetry<T>(
  function flushBandwidthToDb (line 78) | async function flushBandwidthToDb(): Promise<void> {

FILE: server/routers/newt/peers.ts
  function addPeer (line 7) | async function addPeer(
  function deletePeer (line 51) | async function deletePeer(
  function updatePeer (line 93) | async function updatePeer(

FILE: server/routers/newt/sync.ts
  function sendNewtSyncMessage (line 11) | async function sendNewtSyncMessage(newt: Newt, site: Site) {

FILE: server/routers/newt/targets.ts
  function addTargets (line 7) | async function addTargets(
  function removeTargets (line 110) | async function removeTargets(

FILE: server/routers/olm/archiveUserOlm.ts
  function archiveUserOlm (line 19) | async function archiveUserOlm(

FILE: server/routers/olm/buildConfiguration.ts
  function buildSiteConfigurationForOlmClient (line 20) | async function buildSiteConfigurationForOlmClient(

FILE: server/routers/olm/createOlm.ts
  type CreateNewtBody (line 18) | type CreateNewtBody = z.infer<typeof createNewtBodySchema>;
  type CreateNewtResponse (line 20) | type CreateNewtResponse = {
  function createNewt (line 31) | async function createNewt(

FILE: server/routers/olm/createUserOlm.ts
  type CreateOlmBody (line 24) | type CreateOlmBody = z.infer<typeof bodySchema>;
  type CreateOlmResponse (line 26) | type CreateOlmResponse = {
  function createUserOlm (line 49) | async function createUserOlm(

FILE: server/routers/olm/deleteUserOlm.ts
  function deleteUserOlm (line 34) | async function deleteUserOlm(

FILE: server/routers/olm/error.ts
  function sendOlmError (line 93) | async function sendOlmError(

FILE: server/routers/olm/fingerprintingUtils.ts
  function fingerprintSnapshotHash (line 7) | function fingerprintSnapshotHash(fingerprint: any, postures: any): string {
  function handleFingerprintInsertion (line 40) | async function handleFingerprintInsertion(
  function cleanUpOldFingerprintSnapshots (line 218) | async function cleanUpOldFingerprintSnapshots(retentionDays: number) {

FILE: server/routers/olm/getOlmToken.ts
  type OlmGetTokenBody (line 38) | type OlmGetTokenBody = z.infer<typeof olmGetTokenBodySchema>;
  function getOlmToken (line 40) | async function getOlmToken(

FILE: server/routers/olm/getUserOlm.ts
  function getUserOlm (line 36) | async function getUserOlm(

FILE: server/routers/olm/handleOlmPingMessage.ts
  constant OFFLINE_CHECK_INTERVAL (line 18) | const OFFLINE_CHECK_INTERVAL = 30 * 1000;
  constant OFFLINE_THRESHOLD_MS (line 19) | const OFFLINE_THRESHOLD_MS = 2 * 60 * 1000;

FILE: server/routers/olm/listUserOlms.ts
  type ListUserOlmsResponse (line 47) | type ListUserOlmsResponse = {
  function listUserOlms (line 64) | async function listUserOlms(

FILE: server/routers/olm/peers.ts
  function addPeer (line 9) | async function addPeer(
  function deletePeer (line 62) | async function deletePeer(
  function updatePeer (line 99) | async function updatePeer(
  function initPeerAddHandshake (line 150) | async function initPeerAddHandshake(

FILE: server/routers/olm/recoverOlmWithFingerprint.ts
  function recoverOlmWithFingerprint (line 25) | async function recoverOlmWithFingerprint(

FILE: server/routers/olm/sync.ts
  function sendOlmSyncMessage (line 16) | async function sendOlmSyncMessage(olm: Olm, client: Client) {

FILE: server/routers/olm/unarchiveUserOlm.ts
  function unarchiveUserOlm (line 19) | async function unarchiveUserOlm(

FILE: server/routers/org/checkId.ts
  function checkId (line 16) | async function checkId(

FILE: server/routers/org/checkOrgUserAccess.ts
  function queryUser (line 16) | async function queryUser(orgId: string, userId: string) {
  type CheckOrgUserAccessResponse (line 47) | type CheckOrgUserAccessResponse = CheckOrgAccessPolicyResult;
  function checkOrgUserAccess (line 65) | async function checkOrgUserAccess(

FILE: server/routers/org/createOrg.ts
  function createOrg (line 76) | async function createOrg(

FILE: server/routers/org/deleteOrg.ts
  type DeleteOrgResponse (line 17) | type DeleteOrgResponse = {};
  function deleteOrg (line 30) | async function deleteOrg(

FILE: server/routers/org/getOrg.ts
  type GetOrgResponse (line 17) | type GetOrgResponse = {
  function getOrg (line 32) | async function getOrg(

FILE: server/routers/org/getOrgOverview.ts
  type GetOrgOverviewResponse (line 25) | type GetOrgOverviewResponse = {
  function getOrgOverview (line 36) | async function getOrgOverview(

FILE: server/routers/org/listOrgs.ts
  type ListOrgsResponse (line 39) | type ListOrgsResponse = {
  function listOrgs (line 44) | async function listOrgs(

FILE: server/routers/org/listUserOrgs.ts
  type ResponseOrg (line 43) | type ResponseOrg = Org & {
  type ListUserOrgsResponse (line 49) | type ListUserOrgsResponse = {
  function listUserOrgs (line 54) | async function listUserOrgs(

FILE: server/routers/org/pickOrgDefaults.ts
  type PickOrgDefaultsResponse (line 9) | type PickOrgDefaultsResponse = {
  function pickOrgDefaults (line 14) | async function pickOrgDefaults(

FILE: server/routers/org/resetOrgBandwidth.ts
  function resetOrgBandwidth (line 27) | async function resetOrgBandwidth(

FILE: server/routers/org/updateOrg.ts
  function updateOrg (line 63) | async function updateOrg(

FILE: server/routers/orgIdp/types.ts
  type CreateOrgIdpResponse (line 3) | type CreateOrgIdpResponse = {
  type GetOrgIdpResponse (line 8) | type GetOrgIdpResponse = {
  type ListOrgIdpsResponse (line 14) | type ListOrgIdpsResponse = {

FILE: server/routers/remoteExitNode/types.ts
  type CreateRemoteExitNodeResponse (line 3) | type CreateRemoteExitNodeResponse = {
  type PickRemoteExitNodeDefaultsResponse (line 9) | type PickRemoteExitNodeDefaultsResponse = {
  type QuickStartRemoteExitNodeResponse (line 14) | type QuickStartRemoteExitNodeResponse = {
  type ListRemoteExitNodesResponse (line 19) | type ListRemoteExitNodesResponse = {
  type GetRemoteExitNodeResponse (line 34) | type GetRemoteExitNodeResponse = {

FILE: server/routers/resource/addEmailToResourceWhitelist.ts
  function addEmailToResourceWhitelist (line 46) | async function addEmailToResourceWhitelist(

FILE: server/routers/resource/addRoleToResource.ts
  function addRoleToResource (line 46) | async function addRoleToResource(

FILE: server/routers/resource/addUserToResource.ts
  function addUserToResource (line 46) | async function addUserToResource(

FILE: server/routers/resource/authWithAccessToken.ts
  type AuthWithAccessTokenResponse (line 32) | type AuthWithAccessTokenResponse = {
  function authWithAccessToken (line 37) | async function authWithAccessToken(

FILE: server/routers/resource/authWithPassword.ts
  type AuthWithPasswordResponse (line 26) | type AuthWithPasswordResponse = {
  function authWithPassword (line 30) | async function authWithPassword(

FILE: server/routers/resource/authWithPincode.ts
  type AuthWithPincodeResponse (line 25) | type AuthWithPincodeResponse = {
  function authWithPincode (line 29) | async function authWithPincode(

FILE: server/routers/resource/authWithWhitelist.ts
  type AuthWithWhitelistResponse (line 26) | type AuthWithWhitelistResponse = {
  function authWithWhitelist (line 31) | async function authWithWhitelist(

FILE: server/routers/resource/createResource.ts
  type CreateResourceResponse (line 76) | type CreateResourceResponse = Resource;
  function createResource (line 96) | async function createResource(
  function createHttpResource (line 169) | async function createHttpResource(
  function createRawResource (line 328) | async function createRawResource(

FILE: server/routers/resource/createResourceRule.ts
  function createResourceRule (line 48) | async function createResourceRule(

FILE: server/routers/resource/deleteResource.ts
  function deleteResource (line 32) | async function deleteResource(

FILE: server/routers/resource/deleteResourceRule.ts
  function deleteResourceRule (line 29) | async function deleteResourceRule(

FILE: server/routers/resource/getExchangeToken.ts
  type GetExchangeTokenResponse (line 23) | type GetExchangeTokenResponse = {
  function getExchangeToken (line 27) | async function getExchangeToken(

FILE: server/routers/resource/getResource.ts
  function query (line 25) | async function query(resourceId?: number, niceId?: string, orgId?: strin...
  type GetResourceResponse (line 45) | type GetResourceResponse = Omit<
  function getResource (line 80) | async function getResource(

FILE: server/routers/resource/getResourceAuthInfo.ts
  type GetResourceAuthInfoResponse (line 23) | type GetResourceAuthInfoResponse = {
  function getResourceAuthInfo (line 41) | async function getResourceAuthInfo(

FILE: server/routers/resource/getResourceWhitelist.ts
  function queryWhitelist (line 17) | async function queryWhitelist(resourceId: number) {
  type GetResourceWhitelistResponse (line 26) | type GetResourceWhitelistResponse = {
  function getResourceWhitelist (line 41) | async function getResourceWhitelist(

FILE: server/routers/resource/getUserResources.ts
  function getUserResources (line 20) | async function getUserResources(
  type GetUserResourcesResponse (line 256) | type GetUserResourcesResponse = {

FILE: server/routers/resource/listAllResourceNames.ts
  function queryResourceNames (line 17) | function queryResourceNames(orgId: string) {
  type ListResourceNamesResponse (line 28) | type ListResourceNamesResponse = Awaited<
  function listAllResourceNames (line 45) | async function listAllResourceNames(

FILE: server/routers/resource/listResourceRoles.ts
  function query (line 17) | async function query(resourceId: number) {
  type ListResourceRolesResponse (line 30) | type ListResourceRolesResponse = {
  function listResourceRoles (line 45) | async function listResourceRoles(

FILE: server/routers/resource/listResourceRules.ts
  function queryResourceRules (line 32) | function queryResourceRules(resourceId: number) {
  type ListResourceRulesResponse (line 50) | type ListResourceRulesResponse = {
  function listResourceRules (line 67) | async function listResourceRules(

FILE: server/routers/resource/listResourceUsers.ts
  function queryUsers (line 17) | async function queryUsers(resourceId: number) {
  type ListResourceUsersResponse (line 33) | type ListResourceUsersResponse = {
  function listResourceUsers (line 48) | async function listResourceUsers(

FILE: server/routers/resource/listResources.ts
  type ResourceWithTargets (line 119) | type ResourceWithTargets = {
  function queryResourcesBase (line 165) | function queryResourcesBase() {
  type ListResourcesResponse (line 220) | type ListResourcesResponse = PaginatedResponse<{
  function listResources (line 238) | async function listResources(

FILE: server/routers/resource/removeEmailFromResourceWhitelist.ts
  function removeEmailFromResourceWhitelist (line 46) | async function removeEmailFromResourceWhitelist(

FILE: server/routers/resource/removeRoleFromResource.ts
  function removeRoleFromResource (line 46) | async function removeRoleFromResource(

FILE: server/routers/resource/removeUserFromResource.ts
  function removeUserFromResource (line 46) | async function removeUserFromResource(

FILE: server/routers/resource/setResourceHeaderAuth.ts
  function setResourceHeaderAuth (line 46) | async function setResourceHeaderAuth(

FILE: server/routers/resource/setResourcePassword.ts
  function setResourcePassword (line 42) | async function setResourcePassword(

FILE: server/routers/resource/setResourcePincode.ts
  function setResourcePincode (line 46) | async function setResourcePincode(

FILE: server/routers/resource/setResourceRoles.ts
  function setResourceRoles (line 40) | async function setResourceRoles(

FILE: server/routers/resource/setResourceUsers.ts
  function setResourceUsers (line 40) | async function setResourceUsers(

FILE: server/routers/resource/setResourceWhitelist.ts
  function setResourceWhitelist (line 49) | async function setResourceWhitelist(

FILE: server/routers/resource/types.ts
  type GetMaintenanceInfoResponse (line 1) | type GetMaintenanceInfoResponse = {

FILE: server/routers/resource/updateResource.ts
  type UpdateResourceResponse (line 149) | type UpdateResourceResponse = Resource;
  function updateResource (line 198) | async function updateResource(
  function updateHttpResource (line 269) | async function updateHttpResource(
  function updateRawResource (line 446) | async function updateRawResource(

FILE: server/routers/resource/updateResourceRule.ts
  function updateResourceRule (line 55) | async function updateResourceRule(

FILE: server/routers/role/addRoleAction.ts
  function addRoleAction (line 20) | async function addRoleAction(

FILE: server/routers/role/addRoleSite.ts
  function addRoleSite (line 20) | async function addRoleSite(

FILE: server/routers/role/createRole.ts
  type CreateRoleBody (line 40) | type CreateRoleBody = z.infer<typeof createRoleSchema>;
  type CreateRoleResponse (line 42) | type CreateRoleResponse = Role;
  function createRole (line 62) | async function createRole(

FILE: server/routers/role/deleteRole.ts
  function deleteRole (line 39) | async function deleteRole(

FILE: server/routers/role/getRole.ts
  function getRole (line 28) | async function getRole(

FILE: server/routers/role/listRoleActions.ts
  function listRoleActions (line 16) | async function listRoleActions(

FILE: server/routers/role/listRoleResources.ts
  function listRoleResources (line 16) | async function listRoleResources(

FILE: server/routers/role/listRoleSites.ts
  function listRoleSites (line 16) | async function listRoleSites(

FILE: server/routers/role/listRoles.ts
  function queryRoles (line 32) | async function queryRoles(orgId: string, limit: number, offset: number) {
  type ListRolesResponse (line 54) | type ListRolesResponse = {
  function listRoles (line 75) | async function listRoles(

FILE: server/routers/role/removeRoleAction.ts
  function removeRoleAction (line 20) | async function removeRoleAction(

FILE: server/routers/role/removeRoleResource.ts
  function removeRoleResource (line 20) | async function removeRoleResource(

FILE: server/routers/role/removeRoleSite.ts
  function removeRoleSite (line 20) | async function removeRoleSite(

FILE: server/routers/role/updateRole.ts
  type UpdateRoleBody (line 37) | type UpdateRoleBody = z.infer<typeof updateRoleBodySchema>;
  type UpdateRoleResponse (line 39) | type UpdateRoleResponse = Role;
  function updateRole (line 59) | async function updateRole(

FILE: server/routers/serverInfo/getServerInfo.ts
  type GetServerInfoResponse (line 11) | type GetServerInfoResponse = {
  function getServerInfo (line 19) | async function getServerInfo(

FILE: server/routers/site/createSite.ts
  type CreateSiteBody (line 53) | type CreateSiteBody = z.infer<typeof createSiteSchema>;
  type CreateSiteResponse (line 55) | type CreateSiteResponse = Site;
  function createSite (line 75) | async function createSite(

FILE: server/routers/site/deleteSite.ts
  function deleteSite (line 33) | async function deleteSite(

FILE: server/routers/site/getSite.ts
  function query (line 25) | async function query(siteId?: number, niceId?: string, orgId?: string) {
  type GetSiteResponse (line 45) | type GetSiteResponse = NonNullable<
  function getSite (line 77) | async function getSite(

FILE: server/routers/site/listSiteRoles.ts
  function listSiteRoles (line 16) | async function listSiteRoles(

FILE: server/routers/site/listSites.ts
  function getLatestNewtVersion (line 24) | async function getLatestNewtVersion(): Promise<string | null> {
  function querySitesBase (line 141) | function querySitesBase() {
  type SiteWithUpdateAvailable (line 171) | type SiteWithUpdateAvailable = Awaited<ReturnType<typeof querySitesBase>...
  type ListSitesResponse (line 175) | type ListSitesResponse = PaginatedResponse<{
  function listSites (line 191) | async function listSites(

FILE: server/routers/site/pickSiteDefaults.ts
  type PickSiteDefaultsResponse (line 20) | type PickSiteDefaultsResponse = {
  function pickSiteDefaults (line 51) | async function pickSiteDefaults(

FILE: server/routers/site/socketIntegration.ts
  type ContainerNetwork (line 16) | interface ContainerNetwork {
  type ContainerPort (line 27) | interface ContainerPort {
  type Container (line 34) | interface Container {
  function validateSiteIdParams (line 55) | function validateSiteIdParams(params: any) {
  function getSiteAndValidateNewt (line 66) | async function getSiteAndValidateNewt(siteId: number) {
  function getNewtBySiteId (line 87) | async function getNewtBySiteId(siteId: number) {
  function getSiteAndNewt (line 101) | async function getSiteAndNewt(siteId: number) {
  function asyncHandler (line 107) | function asyncHandler(
  function triggerFetch (line 143) | async function triggerFetch(siteId: number) {
  function queryContainers (line 158) | async function queryContainers(siteId: number) {
  function isDockerAvailable (line 172) | async function isDockerAvailable(siteId: number): Promise<boolean> {
  function getDockerStatus (line 181) | async function getDockerStatus(
  function checkSocket (line 199) | async function checkSocket(
  type GetDockerStatusResponse (line 214) | type GetDockerStatusResponse = NonNullable<
  type ListContainersResponse (line 218) | type ListContainersResponse = Awaited<
  type TriggerFetchResponse (line 222) | type TriggerFetchResponse = Awaited<ReturnType<typeof triggerFetch>>;
  function checkDockerSocket (line 245) | async function checkDockerSocket(

FILE: server/routers/site/updateSite.ts
  function updateSite (line 58) | async function updateSite(

FILE: server/routers/siteResource/addClientToSiteResource.ts
  function addClientToSiteResource (line 47) | async function addClientToSiteResource(

FILE: server/routers/siteResource/addRoleToSiteResource.ts
  function addRoleToSiteResource (line 47) | async function addRoleToSiteResource(

FILE: server/routers/siteResource/addUserToSiteResource.ts
  function addUserToSiteResource (line 47) | async function addUserToSiteResource(

FILE: server/routers/siteResource/batchAddClientToSiteResources.ts
  function batchAddClientToSiteResources (line 54) | async function batchAddClientToSiteResources(

FILE: server/routers/siteResource/createSiteResource.ts
  type CreateSiteResourceBody (line 110) | type CreateSiteResourceBody = z.infer<typeof createSiteResourceSchema>;
  type CreateSiteResourceResponse (line 111) | type CreateSiteResourceResponse = SiteResource;
  function createSiteResource (line 131) | async function createSiteResource(

FILE: server/routers/siteResource/deleteSiteResource.ts
  type DeleteSiteResourceResponse (line 18) | type DeleteSiteResourceResponse = {
  function deleteSiteResource (line 33) | async function deleteSiteResource(

FILE: server/routers/siteResource/getSiteResource.ts
  function query (line 25) | async function query(
  type GetSiteResourceResponse (line 60) | type GetSiteResourceResponse = NonNullable<
  function getSiteResource (line 94) | async function getSiteResource(

FILE: server/routers/siteResource/listAllSiteResourcesByOrg.ts
  type ListAllSiteResourcesByOrgResponse (line 74) | type ListAllSiteResourcesByOrgResponse = PaginatedResponse<{
  function querySiteResourcesBase (line 82) | function querySiteResourcesBase() {
  function listAllSiteResourcesByOrg (line 123) | async function listAllSiteResourcesByOrg(

FILE: server/routers/siteResource/listSiteResourceClients.ts
  function queryClients (line 22) | async function queryClients(siteResourceId: number) {
  type ListSiteResourceClientsResponse (line 34) | type ListSiteResourceClientsResponse = {
  function listSiteResourceClients (line 49) | async function listSiteResourceClients(

FILE: server/routers/siteResource/listSiteResourceRoles.ts
  function query (line 22) | async function query(siteResourceId: number) {
  type ListSiteResourceRolesResponse (line 35) | type ListSiteResourceRolesResponse = {
  function listSiteResourceRoles (line 50) | async function listSiteResourceRoles(

FILE: server/routers/siteResource/listSiteResourceUsers.ts
  function queryUsers (line 22) | async function queryUsers(siteResourceId: number) {
  type ListSiteResourceUsersResponse (line 38) | type ListSiteResourceUsersResponse = {
  function listSiteResourceUsers (line 53) | async function listSiteResourceUsers(

FILE: server/routers/siteResource/listSiteResources.ts
  type ListSiteResourcesResponse (line 53) | type ListSiteResourcesResponse = {
  function listSiteResources (line 69) | async function listSiteResources(

FILE: server/routers/siteResource/removeClientFromSiteResource.ts
  function removeClientFromSiteResource (line 47) | async function removeClientFromSiteResource(

FILE: server/routers/siteResource/removeRoleFromSiteResource.ts
  function removeRoleFromSiteResource (line 47) | async function removeRoleFromSiteResource(

FILE: server/routers/siteResource/removeUserFromSiteResource.ts
  function removeUserFromSiteResource (line 47) | async function removeUserFromSiteResource(

FILE: server/routers/siteResource/setSiteResourceClients.ts
  function setSiteResourceClients (line 47) | async function setSiteResourceClients(

FILE: server/routers/siteResource/setSiteResourceRoles.ts
  function setSiteResourceRoles (line 48) | async function setSiteResourceRoles(

FILE: server/routers/siteResource/setSiteResourceUsers.ts
  function setSiteResourceUsers (line 48) | async function setSiteResourceUsers(

FILE: server/routers/siteResource/updateSiteResource.ts
  type UpdateSiteResourceBody (line 117) | type UpdateSiteResourceBody = z.infer<typeof updateSiteResourceSchema>;
  type UpdateSiteResourceResponse (line 118) | type UpdateSiteResourceResponse = SiteResource;
  function updateSiteResource (line 138) | async function updateSiteResource(
  function handleMessagingForUpdatedSiteResource (line 557) | async function handleMessagingForUpdatedSiteResource(

FILE: server/routers/supporterKey/hideSupporterKey.ts
  type HideSupporterKeyResponse (line 8) | type HideSupporterKeyResponse = {
  function hideSupporterKey (line 12) | async function hideSupporterKey(

FILE: server/routers/supporterKey/isSupporterKeyVisible.ts
  type IsSupporterKeyVisibleResponse (line 12) | type IsSupporterKeyVisibleResponse = {
  constant USER_LIMIT (line 17) | const USER_LIMIT = 5;
  function isSupporterKeyVisible (line 19) | async function isSupporterKeyVisible(

FILE: server/routers/supporterKey/validateSupporterKey.ts
  type ValidateSupporterKeyResponse (line 17) | type ValidateSupporterKeyResponse = {
  function validateSupporterKey (line 24) | async function validateSupporterKey(

FILE: server/routers/target/createTarget.ts
  type CreateTargetResponse (line 55) | type CreateTargetResponse = Target & TargetHealthCheck;
  function createTarget (line 75) | async function createTarget(

FILE: server/routers/target/deleteTarget.ts
  function deleteTarget (line 31) | async function deleteTarget(

FILE: server/routers/target/getTarget.ts
  type GetTargetResponse (line 17) | type GetTargetResponse = Target &
  function getTarget (line 33) | async function getTarget(

FILE: server/routers/target/handleHealthcheckStatusMessage.ts
  type TargetHealthStatus (line 8) | interface TargetHealthStatus {
  type HealthcheckStatusMessage (line 29) | interface HealthcheckStatusMessage {

FILE: server/routers/target/helpers.ts
  function pickPort (line 7) | async function pickPort(
  function getAllowedIps (line 47) | async function getAllowedIps(siteId: number) {

FILE: server/routers/target/listTargets.ts
  function queryTargets (line 32) | function queryTargets(resourceId: number) {
  type TargetWithParsedHeaders (line 75) | type TargetWithParsedHeaders = Omit<
  type ListTargetsResponse (line 82) | type ListTargetsResponse = {
  function listTargets (line 99) | async function listTargets(

FILE: server/routers/target/updateTarget.ts
  function updateTarget (line 80) | async function updateTarget(

FILE: server/routers/traefik/configSchema.ts
  type DynamicTraefikConfig (line 1) | type DynamicTraefikConfig = {
  type Http (line 5) | type Http = {
  type Routers (line 11) | type Routers = {
  type Router (line 15) | type Router = {
  type Services (line 22) | type Services = {
  type Service (line 26) | type Service = {
  type LoadBalancer (line 30) | type LoadBalancer = {
  type Server (line 34) | type Server = {
  type Middlewares (line 38) | type Middlewares = {
  type MiddlewarePlugin (line 42) | type MiddlewarePlugin = {
  type Plugin (line 46) | type Plugin = {
  type MiddlewarePluginConfig (line 50) | type MiddlewarePluginConfig = {

FILE: server/routers/traefik/traefikConfigProvider.ts
  function traefikConfigProvider (line 11) | async function traefikConfigProvider(

FILE: server/routers/user/acceptInvite.ts
  type AcceptInviteResponse (line 24) | type AcceptInviteResponse = {
  function acceptInvite (line 29) | async function acceptInvite(

FILE: server/routers/user/addUserAction.ts
  function addUserAction (line 18) | async function addUserAction(

FILE: server/routers/user/addUserRole.ts
  type AddUserRoleResponse (line 20) | type AddUserRoleResponse = z.infer<typeof addUserRoleParamsSchema>;
  function addUserRole (line 33) | async function addUserRole(

FILE: server/routers/user/addUserSite.ts
  function addUserSite (line 17) | async function addUserSite(

FILE: server/routers/user/adminGeneratePasswordResetCode.ts
  type AdminGeneratePasswordResetCodeBody (line 22) | type AdminGeneratePasswordResetCodeBody = z.infer<
  type AdminGeneratePasswordResetCodeResponse (line 26) | type AdminGeneratePasswordResetCodeResponse = {
  function adminGeneratePasswordResetCode (line 32) | async function adminGeneratePasswordResetCode(

FILE: server/routers/user/adminGetUser.ts
  function queryUser (line 27) | async function queryUser(userId: string) {
  type AdminGetUserResponse (line 50) | type AdminGetUserResponse = NonNullable<
  function adminGetUser (line 54) | async function adminGetUser(

FILE: server/routers/user/adminListUsers.ts
  function queryUsers (line 27) | async function queryUsers(limit: number, offset: number) {
  type AdminListUsersResponse (line 49) | type AdminListUsersResponse = {
  function adminListUsers (line 54) | async function adminListUsers(

FILE: server/routers/user/adminRemoveUser.ts
  function adminRemoveUser (line 17) | async function adminRemoveUser(

FILE: server/routers/user/adminUpdateUser2FA.ts
  type UpdateUser2FAResponse (line 21) | type UpdateUser2FAResponse = {
  function updateUser2FA (line 44) | async function updateUser2FA(

FILE: server/routers/user/createOrgUser.ts
  type CreateOrgUserResponse (line 34) | type CreateOrgUserResponse = {};
  function createOrgUser (line 54) | async function createOrgUser(

FILE: server/routers/user/getOrgUser.ts
  function queryUser (line 14) | async function queryUser(orgId: string, userId: string) {
  type GetOrgUserResponse (line 45) | type GetOrgUserResponse = NonNullable<
  function getOrgUser (line 65) | async function getOrgUser(

FILE: server/routers/user/getOrgUserByUsername.ts
  function getOrgUserByUsername (line 46) | async function getOrgUserByUsername(

FILE: server/routers/user/getUser.ts
  function queryUser (line 11) | async function queryUser(userId: string) {
  type GetUserResponse (line 32) | type GetUserResponse = NonNullable<
  function getUser (line 36) | async function getUser(

FILE: server/routers/user/inviteUser.ts
  type InviteUserBody (line 36) | type InviteUserBody = z.infer<typeof inviteUserBodySchema>;
  type InviteUserResponse (line 38) | type InviteUserResponse = {
  function inviteUser (line 61) | async function inviteUser(

FILE: server/routers/user/listInvitations.ts
  function queryInvitations (line 32) | async function queryInvitations(orgId: string, limit: number, offset: nu...
  type ListInvitationsResponse (line 48) | type ListInvitationsResponse = {
  function listInvitations (line 65) | async function listInvitations(

FILE: server/routers/user/listUsers.ts
  function queryUsers (line 33) | async function queryUsers(orgId: string, limit: number, offset: number) {
  type ListUsersResponse (line 63) | type ListUsersResponse = {
  function listUsers (line 80) | async function listUsers(

FILE: server/routers/user/myDevice.ts
  type ResponseOrg (line 17) | type ResponseOrg = {
  type MyDeviceResponse (line 23) | type MyDeviceResponse = {
  function myDevice (line 29) | async function myDevice(

FILE: server/routers/user/removeInvitation.ts
  function removeInvitation (line 29) | async function removeInvitation(

FILE: server/routers/user/removeUserAction.ts
  function removeUserAction (line 21) | async function removeUserAction(

FILE: server/routers/user/removeUserOrg.ts
  function removeUserOrg (line 43) | async function removeUserOrg(

FILE: server/routers/user/removeUserResource.ts
  function removeUserResource (line 17) | async function removeUserResource(

FILE: server/routers/user/removeUserSite.ts
  function removeUserSite (line 20) | async function removeUserSite(

FILE: server/routers/user/updateOrgUser.ts
  function updateOrgUser (line 43) | async function updateOrgUser(

FILE: server/routers/ws/checkRoundTripMessage.ts
  function checkRoundTripMessage (line 33) | async function checkRoundTripMessage(

FILE: server/routers/ws/handleRoundTripMessage.ts
  type RoundTripCompleteMessage (line 6) | interface RoundTripCompleteMessage {

FILE: server/routers/ws/types.ts
  type WebSocketRequest (line 16) | interface WebSocketRequest extends IncomingMessage {
  type ClientType (line 20) | type ClientType = "newt" | "olm" | "remoteExitNode";
  type AuthenticatedWebSocket (line 22) | interface AuthenticatedWebSocket extends WebSocket {
  type TokenPayload (line 31) | interface TokenPayload {
  type WSMessage (line 37) | interface WSMessage {
  type HandlerResponse (line 43) | interface HandlerResponse {
  type HandlerContext (line 51) | interface HandlerContext {
  type MessageHandler (line 69) | type MessageHandler = (
  type SendMessageOptions (line 74) | interface SendMessageOptions {
  type RedisMessage (line 80) | interface RedisMessage {

FILE: server/routers/ws/ws.ts
  type PublicTokenPayload (line 25) | interface PublicTokenPayload {
  constant NODE_ID (line 35) | const NODE_ID = uuidv4();

FILE: server/setup/clearStaleData.ts
  function clearStaleData (line 16) | async function clearStaleData() {

FILE: server/setup/copyInConfig.ts
  function copyInConfig (line 7) | async function copyInConfig() {
  function copyInDomains (line 40) | async function copyInDomains() {

FILE: server/setup/ensureActions.ts
  function ensureActions (line 7) | async function ensureActions() {

FILE: server/setup/ensureSetupToken.ts
  method read (line 8) | read(bytes: Uint8Array): void {
  function generateToken (line 13) | function generateToken(): string {
  function validateToken (line 19) | function validateToken(token: string): boolean {
  function generateId (line 24) | function generateId(length: number): string {
  function showSetupToken (line 29) | function showSetupToken(token: string, source: string): void {
  function ensureSetupToken (line 36) | async function ensureSetupToken() {

FILE: server/setup/index.ts
  function runSetupFunctions (line 6) | async function runSetupFunctions() {

FILE: server/setup/migrationsPg.ts
  function run (line 53) | async function run() {
  function runMigrations (line 58) | async function runMigrations() {
  function executeScripts (line 110) | async function executeScripts() {

FILE: server/setup/migrationsSqlite.ts
  function run (line 84) | async function run() {
  function backupDb (line 89) | function backupDb() {
  function runMigrations (line 110) | async function runMigrations() {
  function executeScripts (line 151) | async function executeScripts() {

FILE: server/setup/scriptsPg/1.10.0.ts
  function migration (line 9) | async function migration() {
  function generateName (line 134) | function generateName(): string {

FILE: server/setup/scriptsPg/1.10.2.ts
  function migration (line 7) | async function migration() {

FILE: server/setup/scriptsPg/1.11.0.ts
  function migration (line 8) | async function migration() {

FILE: server/setup/scriptsPg/1.11.1.ts
  function migration (line 6) | async function migration() {

FILE: server/setup/scriptsPg/1.12.0.ts
  function migration (line 6) | async function migration() {

FILE: server/setup/scriptsPg/1.13.0.ts
  function generateName (line 18) | function generateName(): string {
  function migration (line 33) | async function migration() {

FILE: server/setup/scriptsPg/1.14.0.ts
  function migration (line 7) | async function migration() {

FILE: server/setup/scriptsPg/1.15.0.ts
  function migration (line 7) | async function migration() {

FILE: server/setup/scriptsPg/1.15.3.ts
  function migration (line 7) | async function migration() {

FILE: server/setup/scriptsPg/1.15.4.ts
  function migration (line 7) | async function migration() {

FILE: server/setup/scriptsPg/1.16.0.ts
  function getServerSecret (line 11) | function getServerSecret(): string {
  function migration (line 55) | async function migration() {

FILE: server/setup/scriptsPg/1.6.0.ts
  function migration (line 9) | async function migration() {

FILE: server/setup/scriptsPg/1.7.0.ts
  function migration (line 6) | async function migration() {

FILE: server/setup/scriptsPg/1.8.0.ts
  function migration (line 6) | async function migration() {

FILE: server/setup/scriptsPg/1.9.0.ts
  function migration (line 6) | async function migration() {

FILE: server/setup/scriptsSqlite/1.0.0-beta1.ts
  function migration (line 1) | async function migration() {

FILE: server/setup/scriptsSqlite/1.0.0-beta10.ts
  function migration (line 5) | async function migration() {

FILE: server/setup/scriptsSqlite/1.0.0-beta12.ts
  function migration (line 7) | async function migration() {

FILE: server/setup/scriptsSqlite/1.0.0-beta13.ts
  function migration (line 6) | async function migration() {

FILE: server/setup/scriptsSqlite/1.0.0-beta15.ts
  function migration (line 10) | async function migration() {

FILE: server/setup/scriptsSqlite/1.0.0-beta2.ts
  function migration (line 5) | async function migration() {
  function getBaseDomain (line 48) | function getBaseDomain(url: string): string {

FILE: server/setup/scriptsSqlite/1.0.0-beta3.ts
  function migration (line 5) | async function migration() {

FILE: server/setup/scriptsSqlite/1.0.0-beta5.ts
  function migration (line 8) | async function migration() {

FILE: server/setup/scriptsSqlite/1.0.0-beta6.ts
  function migration (line 5) | async function migration() {

FILE: server/setup/scriptsSqlite/1.0.0-beta9.ts
  function migration (line 20) | async function migration() {

FILE: server/setup/scriptsSqlite/1.0.0.ts
  function migration (line 10) | async function migration() {

FILE: server/setup/scriptsSqlite/1.1.0.ts
  function migration (line 6) | async function migration() {

FILE: server/setup/scriptsSqlite/1.10.0.ts
  function migration (line 8) | async function migration() {
  function generateName (line 115) | function generateName(): string {

FILE: server/setup/scriptsSqlite/1.10.1.ts
  function migration (line 7) | async function migration() {

FILE: server/setup/scriptsSqlite/1.10.2.ts
  function migration (line 7) | async function migration() {

FILE: server/setup/scriptsSqlite/1.11.0.ts
  function migration (line 9) | async function migration() {

FILE: server/setup/scriptsSqlite/1.11.1.ts
  function migration (line 7) | async function migration() {

FILE: server/setup/scriptsSqlite/1.12.0.ts
  function migration (line 7) | async function migration() {

FILE: server/setup/scriptsSqlite/1.13.0.ts
  function generateName (line 17) | function generateName(): string {
  function migration (line 32) | async function migration() {

FILE: server/setup/scriptsSqlite/1.14.0.ts
  function migration (line 7) | async function migration() {

FILE: server/setup/scriptsSqlite/1.15.0.ts
  function migration (line 7) | async function migration() {

FILE: server/setup/scriptsSqlite/1.15.3.ts
  function migration (line 7) | async function migration() {

FILE: server/setup/scriptsSqlite/1.15.4.ts
  function migration (line 7) | async function migration() {

FILE: server/setup/scriptsSqlite/1.16.0.ts
  function getServerSecret (line 11) | function getServerSecret(): string {
  function migration (line 55) | async function migration() {

FILE: server/setup/scriptsSqlite/1.2.0.ts
  function migration (line 12) | async function migration() {

FILE: server/setup/scriptsSqlite/1.3.0.ts
  function migration (line 11) | async function migration() {
  function generateIdFromEntropySize (line 200) | function generateIdFromEntropySize(size: number): string {

FILE: server/setup/scriptsSqlite/1.5.0.ts
  function migration (line 10) | async function migration() {

FILE: server/setup/scriptsSqlite/1.6.0.ts
  function migration (line 9) | async function migration() {

FILE: server/setup/scriptsSqlite/1.7.0.ts
  function migration (line 7) | async function migration() {

FILE: server/setup/scriptsSqlite/1.8.0.ts
  function migration (line 7) | async function migration() {

FILE: server/setup/scriptsSqlite/1.9.0.ts
  function migration (line 7) | async function migration() {

FILE: server/types/ArrayElement.ts
  type ArrayElement (line 1) | type ArrayElement<ArrayType extends readonly unknown[]> =

FILE: server/types/Auth.ts
  type AuthenticatedRequest (line 5) | interface AuthenticatedRequest extends Request {

FILE: server/types/ErrorResponse.ts
  type ErrorResponse (line 3) | interface ErrorResponse extends MessageResponse<null> {

FILE: server/types/HttpCode.ts
  type HttpCode (line 1) | enum HttpCode {

FILE: server/types/MessageResponse.ts
  type ResponseT (line 1) | interface ResponseT<T> {

FILE: server/types/Pagination.ts
  type Pagination (line 1) | type Pagination = { total: number; pageSize: number; page: number };
  type PaginatedResponse (line 3) | type PaginatedResponse<T> = T & {

FILE: server/types/Response.ts
  type ResponseT (line 1) | interface ResponseT<T> {

FILE: server/types/Tiers.ts
  type Tier (line 1) | type Tier = "tier1" | "tier2" | "tier3" | "enterprise";

FILE: server/types/UserTypes.ts
  type UserType (line 1) | enum UserType {

FILE: src/actions/server.ts
  type CookieOptions (line 7) | type CookieOptions = {
  function parseSetCookieString (line 17) | function parseSetCookieString(
  function makeApiRequest (line 71) | async function makeApiRequest<T>(
  type LoginRequest (line 203) | type LoginRequest = {
  type LoginResponse (line 210) | type LoginResponse = {
  type SecurityKeyStartRequest (line 217) | type SecurityKeyStartRequest = {
  type SecurityKeyStartResponse (line 221) | type SecurityKeyStartResponse = {
  type SecurityKeyVerifyRequest (line 230) | type SecurityKeyVerifyRequest = {
  type SecurityKeyVerifyResponse (line 234) | type SecurityKeyVerifyResponse = {
  function loginProxy (line 239) | async function loginProxy(
  function securityKeyStartProxy (line 251) | async function securityKeyStartProxy(
  function securityKeyVerifyProxy (line 263) | async function securityKeyVerifyProxy(
  type ResourcePasswordRequest (line 287) | type ResourcePasswordRequest = {
  type ResourcePasswordResponse (line 291) | type ResourcePasswordResponse = {
  type ResourcePincodeRequest (line 295) | type ResourcePincodeRequest = {
  type ResourcePincodeResponse (line 299) | type ResourcePincodeResponse = {
  type ResourceWhitelistRequest (line 303) | type ResourceWhitelistRequest = {
  type ResourceWhitelistResponse (line 308) | type ResourceWhitelistResponse = {
  type ResourceAccessResponse (line 313) | type ResourceAccessResponse = {
  function resourcePasswordProxy (line 318) | async function resourcePasswordProxy(
  function resourcePincodeProxy (line 330) | async function resourcePincodeProxy(
  function resourceWhitelistProxy (line 342) | async function resourceWhitelistProxy(
  function resourceAccessProxy (line 358) | async function resourceAccessProxy(
  type GenerateOidcUrlRequest (line 373) | type GenerateOidcUrlRequest = {
  type GenerateOidcUrlResponse (line 377) | type GenerateOidcUrlResponse = {
  type ValidateOidcUrlCallbackRequest (line 381) | type ValidateOidcUrlCallbackRequest = {
  type ValidateOidcUrlCallbackResponse (line 387) | type ValidateOidcUrlCallbackResponse = {
  function validateOidcUrlCallbackProxy (line 391) | async function validateOidcUrlCallbackProxy(
  function generateOidcUrlProxy (line 410) | async function generateOidcUrlProxy(

FILE: src/app/[orgId]/layout.tsx
  function OrgLayout (line 24) | async function OrgLayout(props: {

FILE: src/app/[orgId]/page.tsx
  type OrgPageProps (line 14) | type OrgPageProps = {
  function OrgPage (line 18) | async function OrgPage(props: OrgPageProps) {

FILE: src/app/[orgId]/settings/(private)/access/approvals/page.tsx
  type ApprovalFeedPageProps (line 16) | interface ApprovalFeedPageProps {
  function ApprovalFeedPage (line 20) | async function ApprovalFeedPage(props: ApprovalFeedPageProps) {

FILE: src/app/[orgId]/settings/(private)/billing/layout.tsx
  type BillingSettingsProps (line 11) | type BillingSettingsProps = {
  function BillingSettingsPage (line 16) | async function BillingSettingsPage({

FILE: src/app/[orgId]/settings/(private)/billing/page.tsx
  type PlanId (line 60) | type PlanId = "basic" | "home" | "team" | "business" | "enterprise";
  type PlanOption (line 62) | type PlanOption = {
  function BillingPage (line 195) | function BillingPage() {

FILE: src/app/[orgId]/settings/(private)/idp/[idpId]/general/page.tsx
  function GeneralPage (line 51) | function GeneralPage() {

FILE: src/app/[orgId]/settings/(private)/idp/[idpId]/layout.tsx
  type SettingsLayoutProps (line 10) | interface SettingsLayoutProps {
  function SettingsLayout (line 15) | async function SettingsLayout(props: SettingsLayoutProps) {

FILE: src/app/[orgId]/settings/(private)/idp/[idpId]/page.tsx
  function IdpPage (line 3) | async function IdpPage(props: {

FILE: src/app/[orgId]/settings/(private)/idp/create/page.tsx
  function Page (line 46) | function Page() {

FILE: src/app/[orgId]/settings/(private)/idp/layout.tsx
  type LayoutProps (line 1) | interface LayoutProps {
  function Layout (line 6) | async function Layout(props: LayoutProps) {

FILE: src/app/[orgId]/settings/(private)/idp/page.tsx
  type OrgIdpPageProps (line 11) | type OrgIdpPageProps = {
  function OrgIdpPage (line 17) | async function OrgIdpPage(props: OrgIdpPageProps) {

FILE: src/app/[orgId]/settings/(private)/license/layout.tsx
  type LicensesSettingsProps (line 10) | type LicensesSettingsProps = {
  function LicensesSetingsLayoutProps (line 15) | async function LicensesSetingsLayoutProps({

FILE: src/app/[orgId]/settings/(private)/license/page.tsx
  type Props (line 7) | type Props = {
  function Page (line 13) | async function Page({ params }: Props) {

FILE: src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/credentials/page.tsx
  function CredentialsPage (line 39) | function CredentialsPage() {

FILE: src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/layout.tsx
  type SettingsLayoutProps (line 12) | interface SettingsLayoutProps {
  function SettingsLayout (line 17) | async function SettingsLayout(props: SettingsLayoutProps) {

FILE: src/app/[orgId]/settings/(private)/remote-exit-nodes/[remoteExitNodeId]/page.tsx
  function RemoteExitNodePage (line 3) | async function RemoteExitNodePage(props: {

FILE: src/app/[orgId]/settings/(private)/remote-exit-nodes/create/page.tsx
  function CreateRemoteExitNodePage (line 43) | function CreateRemoteExitNodePage() {

FILE: src/app/[orgId]/settings/(private)/remote-exit-nodes/page.tsx
  type RemoteExitNodesPageProps (line 11) | type RemoteExitNodesPageProps = {
  function RemoteExitNodesPage (line 17) | async function RemoteExitNodesPage(

FILE: src/app/[orgId]/settings/access/invitations/page.tsx
  type InvitationsPageProps (line 16) | type InvitationsPageProps = {
  function InvitationsPage (line 22) | async function InvitationsPage(props: InvitationsPageProps) {

FILE: src/app/[orgId]/settings/access/layout.tsx
  type AccessLayoutProps (line 1) | interface AccessLayoutProps {
  function ResourceLayout (line 8) | async function ResourceLayout(props: AccessLayoutProps) {

FILE: src/app/[orgId]/settings/access/page.tsx
  type AccessPageProps (line 3) | type AccessPageProps = {
  function AccessPage (line 7) | async function AccessPage(props: AccessPageProps) {

FILE: src/app/[orgId]/settings/access/roles/page.tsx
  type RolesPageProps (line 12) | type RolesPageProps = {
  function RolesPage (line 18) | async function RolesPage(props: RolesPageProps) {

FILE: src/app/[orgId]/settings/access/users/[userId]/access-controls/page.tsx
  function AccessControlsPage (line 48) | function AccessControlsPage() {

FILE: src/app/[orgId]/settings/access/users/[userId]/layout.tsx
  type UserLayoutProps (line 12) | interface UserLayoutProps {
  function UserLayoutProps (line 17) | async function UserLayoutProps(props: UserLayoutProps) {

FILE: src/app/[orgId]/settings/access/users/[userId]/page.tsx
  function UserPage (line 3) | async function UserPage(props: {

FILE: src/app/[orgId]/settings/access/users/create/page.tsx
  type UserType (line 53) | type UserType = "internal" | "oidc";
  type IdpOption (line 55) | interface IdpOption {
  type UserOption (line 62) | interface UserOption {
  function Page (line 72) | function Page() {

FILE: src/app/[orgId]/settings/access/users/page.tsx
  type UsersPageProps (line 16) | type UsersPageProps = {
  function UsersPage (line 22) | async function UsersPage(props: UsersPageProps) {

FILE: src/app/[orgId]/settings/api-keys/[apiKeyId]/layout.tsx
  type SettingsLayoutProps (line 11) | interface SettingsLayoutProps {
  function SettingsLayout (line 16) | async function SettingsLayout(props: SettingsLayoutProps) {

FILE: src/app/[orgId]/settings/api-keys/[apiKeyId]/page.tsx
  function ApiKeysPage (line 3) | async function ApiKeysPage(props: {

FILE: src/app/[orgId]/settings/api-keys/[apiKeyId]/permissions/page.tsx
  function Page (line 23) | function Page() {

FILE: src/app/[orgId]/settings/api-keys/create/page.tsx
  function Page (line 51) | function Page() {

FILE: src/app/[orgId]/settings/api-keys/page.tsx
  type ApiKeyPageProps (line 11) | type ApiKeyPageProps = {
  function ApiKeysPage (line 17) | async function ApiKeysPage(props: ApiKeyPageProps) {

FILE: src/app/[orgId]/settings/blueprints/[blueprintId]/page.tsx
  type BluePrintsPageProps (line 15) | type BluePrintsPageProps = {
  function BluePrintDetailPage (line 23) | async function BluePrintDetailPage(props: BluePrintsPageProps) {

FILE: src/app/[orgId]/settings/blueprints/create/page.tsx
  type CreateBlueprintPageProps (line 10) | interface CreateBlueprintPageProps {
  function CreateBlueprintPage (line 18) | async function CreateBlueprintPage(

FILE: src/app/[orgId]/settings/blueprints/page.tsx
  type BluePrintsPageProps (line 15) | type BluePrintsPageProps = {
  function BluePrintsPage (line 23) | async function BluePrintsPage(props: BluePrintsPageProps) {

FILE: src/app/[orgId]/settings/clients/layout.tsx
  type SettingsLayoutProps (line 6) | interface SettingsLayoutProps {
  function SettingsLayout (line 11) | async function SettingsLayout(props: SettingsLayoutProps) {

FILE: src/app/[orgId]/settings/clients/machine/[niceId]/credentials/page.tsx
  function CredentialsPage (line 36) | function CredentialsPage() {

FILE: src/app/[orgId]/settings/clients/machine/[niceId]/general/page.tsx
  type GeneralFormValues (line 43) | type GeneralFormValues = z.infer<typeof GeneralFormSchema>;
  function GeneralPage (line 45) | function GeneralPage() {

FILE: src/app/[orgId]/settings/clients/machine/[niceId]/layout.tsx
  type SettingsLayoutProps (line 12) | type SettingsLayoutProps = {
  function SettingsLayout (line 17) | async function SettingsLayout(props: SettingsLayoutProps) {

FILE: src/app/[orgId]/settings/clients/machine/[niceId]/page.tsx
  function ClientPage (line 3) | async function ClientPage(props: {

FILE: src/app/[orgId]/settings/clients/machine/create/page.tsx
  type ClientType (line 49) | type ClientType = "olm";
  type TunnelTypeOption (line 51) | interface TunnelTypeOption {
  function Page (line 58) | function Page() {

FILE: src/app/[orgId]/settings/clients/machine/page.tsx
  type ClientsPageProps (line 12) | type ClientsPageProps = {
  function ClientsPage (line 19) | async function ClientsPage(props: ClientsPageProps) {

FILE: src/app/[orgId]/settings/clients/page.tsx
  type ClientsPageProps (line 3) | type ClientsPageProps = {
  function ClientsPage (line 10) | async function ClientsPage(props: ClientsPageProps) {

FILE: src/app/[orgId]/settings/clients/user/[niceId]/general/page.tsx
  function formatTimestamp (line 45) | function formatTimestamp(timestamp: number | null | undefined): string {
  function formatPlatform (line 50) | function formatPlatform(platform: string | null | undefined): string {
  function getPlatformIcon (line 63) | function getPlatformIcon(platform: string | null | undefined) {
  type FieldConfig (line 81) | type FieldConfig = {
  function getPlatformFieldConfig (line 86) | function getPlatformFieldConfig(
  function GeneralPage (line 145) | function GeneralPage() {

FILE: src/app/[orgId]/settings/clients/user/[niceId]/layout.tsx
  type SettingsLayoutProps (line 12) | type SettingsLayoutProps = {
  function SettingsLayout (line 17) | async function SettingsLayout(props: SettingsLayoutProps) {

FILE: src/app/[orgId]/settings/clients/user/[niceId]/page.tsx
  function ClientPage (line 3) | async function ClientPage(props: {

FILE: src/app/[orgId]/settings/clients/user/page.tsx
  type ClientsPageProps (line 11) | type ClientsPageProps = {
  function ClientsPage (line 18) | async function ClientsPage(props: ClientsPageProps) {

FILE: src/app/[orgId]/settings/domains/[domainId]/page.tsx
  type DomainSettingsPageProps (line 14) | interface DomainSettingsPageProps {
  function DomainSettingsPage (line 18) | async function DomainSettingsPage({

FILE: src/app/[orgId]/settings/domains/page.tsx
  type Props (line 15) | type Props = {
  function DomainsPage (line 19) | async function DomainsPage(props: Props) {

FILE: src/app/[orgId]/settings/general/auth-page/page.tsx
  type AuthPageProps (line 16) | interface AuthPageProps {
  function AuthPage (line 20) | async function AuthPage(props: AuthPageProps) {

FILE: src/app/[orgId]/settings/general/layout.tsx
  type GeneralSettingsProps (line 15) | type GeneralSettingsProps = {
  function GeneralSettingsPage (line 20) | async function GeneralSettingsPage({

FILE: src/app/[orgId]/settings/general/page.tsx
  function GeneralPage (line 48) | function GeneralPage() {
  type SectionFormProps (line 58) | type SectionFormProps = {
  function DeleteForm (line 62) | function DeleteForm({ org }: SectionFormProps) {
  function GeneralSectionForm (line 160) | function GeneralSectionForm({ org }: SectionFormProps) {

FILE: src/app/[orgId]/settings/general/security/page.tsx
  constant SESSION_LENGTH_OPTIONS (line 49) | const SESSION_LENGTH_OPTIONS = [
  constant PASSWORD_EXPIRY_OPTIONS (line 65) | const PASSWORD_EXPIRY_OPTIONS = [
  constant LOG_RETENTION_OPTIONS (line 85) | const LOG_RETENTION_OPTIONS = [
  type SectionFormProps (line 100) | type SectionFormProps = {
  function SecurityPage (line 104) | function SecurityPage() {
  function LogRetentionSectionForm (line 117) | function LogRetentionSectionForm({ org }: SectionFormProps) {
  function SecuritySettingsSectionForm (line 497) | function SecuritySettingsSectionForm({ org }: SectionFormProps) {

FILE: src/app/[orgId]/settings/layout.tsx
  type SettingsLayoutProps (line 27) | interface SettingsLayoutProps {
  function SettingsLayout (line 32) | async function SettingsLayout(props: SettingsLayoutProps) {

FILE: src/app/[orgId]/settings/logs/access/page.tsx
  function GeneralPage (line 24) | function GeneralPage() {

FILE: src/app/[orgId]/settings/logs/action/page.tsx
  function GeneralPage (line 22) | function GeneralPage() {

FILE: src/app/[orgId]/settings/logs/analytics/page.tsx
  type AnalyticsPageProps (line 6) | interface AnalyticsPageProps {
  function AnalyticsPage (line 11) | async function AnalyticsPage(props: AnalyticsPageProps) {

FILE: src/app/[orgId]/settings/logs/layout.tsx
  type GeneralSettingsProps (line 5) | type GeneralSettingsProps = {
  function GeneralSettingsPage (line 10) | async function GeneralSettingsPage({

FILE: src/app/[orgId]/settings/logs/page.tsx
  function GeneralPage (line 1) | function GeneralPage() {

FILE: src/app/[orgId]/settings/logs/request/page.tsx
  function GeneralPage (line 21) | function GeneralPage() {

FILE: src/app/[orgId]/settings/not-found.tsx
  function NotFound (line 3) | async function NotFound() {

FILE: src/app/[orgId]/settings/page.tsx
  type OrgPageProps (line 3) | type OrgPageProps = {
  function SettingsPage (line 7) | async function SettingsPage(props: OrgPageProps) {

FILE: src/app/[orgId]/settings/resources/client/page.tsx
  type ClientResourcesPageProps (line 15) | interface ClientResourcesPageProps {
  function ClientResourcesPage (line 20) | async function ClientResourcesPage(

FILE: src/app/[orgId]/settings/resources/page.tsx
  type ResourcesPageProps (line 3) | interface ResourcesPageProps {
  function ResourcesPage (line 7) | async function ResourcesPage(props: ResourcesPageProps) {

FILE: src/app/[orgId]/settings/resources/proxy/[niceId]/authentication/page.tsx
  function ResourceAuthenticationPage (line 89) | function ResourceAuthenticationPage() {
  type OneTimePasswordFormSectionProps (line 791) | type OneTimePasswordFormSectionProps = Pick<
  function OneTimePasswordFormSection (line 799) | function OneTimePasswordFormSection({

FILE: src/app/[orgId]/settings/resources/proxy/[niceId]/general/page.tsx
  type MaintenanceSectionFormProps (line 66) | type MaintenanceSectionFormProps = {
  function MaintenanceSectionForm (line 71) | function MaintenanceSectionForm({
  function GeneralForm (line 439) | function GeneralForm() {

FILE: src/app/[orgId]/settings/resources/proxy/[niceId]/layout.tsx
  type ResourceLayoutProps (line 20) | interface ResourceLayoutProps {
  function ResourceLayout (line 25) | async function ResourceLayout(props: ResourceLayoutProps) {

FILE: src/app/[orgId]/settings/resources/proxy/[niceId]/page.tsx
  function ResourcePage (line 3) | async function ResourcePage(props: {

FILE: src/app/[orgId]/settings/resources/proxy/[niceId]/proxy/page.tsx
  type LocalTarget (line 107) | type LocalTarget = Omit<
  function ReverseProxyTargetsPage (line 116) | function ReverseProxyTargetsPage(props: {
  function ProxyResourceTargetsForm (line 162) | function ProxyResourceTargetsForm({
  function ProxyResourceHttpForm (line 1009) | function ProxyResourceHttpForm({
  function ProxyResourceProtocolForm (line 1305) | function ProxyResourceProtocolForm({

FILE: src/app/[orgId]/settings/resources/proxy/[niceId]/rules/page.tsx
  type LocalRule (line 101) | type LocalRule = ArrayElement<ListResourceRulesResponse["rules"]> & {
  function ResourceRules (line 106) | function ResourceRules(props: {

FILE: src/app/[orgId]/settings/resources/proxy/create/page.tsx
  type ResourceType (line 187) | type ResourceType = "http" | "raw";
  type ResourceTypeOption (line 189) | interface ResourceTypeOption {
  type LocalTarget (line 196) | type LocalTarget = Omit<
  function Page (line 205) | function Page() {

FILE: src/app/[orgId]/settings/resources/proxy/page.tsx
  type ProxyResourcesPageProps (line 17) | interface ProxyResourcesPageProps {
  function ProxyResourcesPage (line 22) | async function ProxyResourcesPage(

FILE: src/app/[orgId]/settings/share-links/page.tsx
  type ShareLinksPageProps (line 15) | type ShareLinksPageProps = {
  function ShareLinksPage (line 21) | async function ShareLinksPage(props: ShareLinksPageProps) {

FILE: src/app/[orgId]/settings/sites/[niceId]/credentials/page.tsx
  function CredentialsPage (line 43) | function CredentialsPage() {

FILE: src/app/[orgId]/settings/sites/[niceId]/general/page.tsx
  type GeneralFormValues (line 44) | type GeneralFormValues = z.infer<typeof GeneralFormSchema>;
  function GeneralPage (line 46) | function GeneralPage() {

FILE: src/app/[orgId]/settings/sites/[niceId]/layout.tsx
  type SettingsLayoutProps (line 13) | interface SettingsLayoutProps {
  function SettingsLayout (line 18) | async function SettingsLayout(props: SettingsLayoutProps) {

FILE: src/app/[orgId]/settings/sites/[niceId]/page.tsx
  function SitePage (line 3) | async function SitePage(props: {

FILE: src/app/[orgId]/settings/sites/[niceId]/wireguardConfig.ts
  function gf (line 6) | function gf(init: number[] | undefined = undefined) {
  function pack (line 14) | function pack(o: Uint8Array, n: Float64Array) {
  function carry (line 39) | function carry(o: Float64Array) {
  function cswap (line 47) | function cswap(p: Float64Array, q: Float64Array, b: number) {
  function add (line 57) | function add(o: Float64Array, a: Float64Array, b: Float64Array) {
  function subtract (line 61) | function subtract(o: Float64Array, a: Float64Array, b: Float64Array) {
  function multmod (line 65) | function multmod(o: Float64Array, a: Float64Array, b: Float64Array) {
  function invert (line 76) | function invert(o: Float64Array, i: Float64Array) {
  function clamp (line 86) | function clamp(z: Uint8Array) {
  function generatePublicKey (line 91) | function generatePublicKey(privateKey: Uint8Array) {
  function generatePresharedKey (line 135) | function generatePresharedKey() {
  function generatePrivateKey (line 141) | function generatePrivateKey() {
  function encodeBase64 (line 147) | function encodeBase64(dest: Uint8Array, src: Uint8Array) {
  function keyToBase64 (line 164) | function keyToBase64(key: Uint8Array) {
  function generateKeypair (line 177) | function generateKeypair() {

FILE: src/app/[orgId]/settings/sites/create/page.tsx
  type SiteType (line 67) | type SiteType = "newt" | "wireguard" | "local";
  type TunnelTypeOption (line 69) | interface TunnelTypeOption {
  type RemoteExitNodeOption (line 76) | interface RemoteExitNodeOption {
  type CommandItem (line 83) | type CommandItem = string | { title: string; command: string };
  type Commands (line 85) | type Commands = {
  type Platform (line 103) | type Platform = (typeof platforms)[number];
  function Page (line 105) | function Page() {

FILE: src/app/[orgId]/settings/sites/page.tsx
  type SitesPageProps (line 10) | type SitesPageProps = {
  function SitesPage (line 17) | async function SitesPage(props: SitesPageProps) {

FILE: src/app/admin/api-keys/[apiKeyId]/layout.tsx
  type SettingsLayoutProps (line 11) | interface SettingsLayoutProps {
  function SettingsLayout (line 16) | async function SettingsLayout(props: SettingsLayoutProps) {

FILE: src/app/admin/api-keys/[apiKeyId]/page.tsx
  function ApiKeysPage (line 3) | async function ApiKeysPage(props: {

FILE: src/app/admin/api-keys/[apiKeyId]/permissions/page.tsx
  function Page (line 23) | function Page() {

FILE: src/app/admin/api-keys/create/page.tsx
  function Page (line 50) | function Page() {

FILE: src/app/admin/api-keys/page.tsx
  type ApiKeyPageProps (line 9) | type ApiKeyPageProps = {};
  function ApiKeysPage (line 13) | async function ApiKeysPage(props: ApiKeyPageProps) {

FILE: src/app/admin/idp/[idpId]/general/page.tsx
  function GeneralPage (line 48) | function GeneralPage() {

FILE: src/app/admin/idp/[idpId]/layout.tsx
  type SettingsLayoutProps (line 10) | interface SettingsLayoutProps {
  function SettingsLayout (line 15) | async function SettingsLayout(props: SettingsLayoutProps) {

FILE: src/app/admin/idp/[idpId]/page.tsx
  function IdpPage (line 3) | async function IdpPage(props: {

FILE: src/app/admin/idp/[idpId]/policies/page.tsx
  type Organization (line 68) | type Organization = {
  function PoliciesPage (line 73) | function PoliciesPage() {

FILE: src/app/admin/idp/create/page.tsx
  function Page (line 42) | function Page() {

FILE: src/app/admin/idp/page.tsx
  function IdpPage (line 8) | async function IdpPage() {

FILE: src/app/admin/layout.tsx
  type LayoutProps (line 23) | interface LayoutProps {
  function AdminLayout (line 27) | async function AdminLayout(props: LayoutProps) {

FILE: src/app/admin/license/layout.tsx
  type LayoutProps (line 6) | interface LayoutProps {
  function AdminLicenseLayout (line 10) | async function AdminLicenseLayout(props: LayoutProps) {

FILE: src/app/admin/license/page.tsx
  function obfuscateLicenseKey (line 54) | function obfuscateLicenseKey(key: string): string {
  function LicensePage (line 61) | function LicensePage() {

FILE: src/app/admin/page.tsx
  type AdminPageProps (line 5) | type AdminPageProps = {};
  function OrgPage (line 7) | async function OrgPage(props: AdminPageProps) {

FILE: src/app/admin/users/AdminUsersTable.tsx
  type GlobalUserRow (line 22) | type GlobalUserRow = {
  type Props (line 35) | type Props = {
  function UsersTable (line 39) | function UsersTable({ users }: Props) {

FILE: src/app/admin/users/[userId]/general/page.tsx
  function GeneralPage (line 23) | function GeneralPage() {

FILE: src/app/admin/users/[userId]/layout.tsx
  type UserLayoutProps (line 12) | interface UserLayoutProps {
  function UserLayoutProps (line 17) | async function UserLayoutProps(props: UserLayoutProps) {

FILE: src/app/admin/users/[userId]/page.tsx
  function UserPage (line 3) | async function UserPage(props: {

FILE: src/app/admin/users/page.tsx
  type PageProps (line 11) | type PageProps = {
  function UsersPage (line 17) | async function UsersPage(props: PageProps) {

FILE: src/app/auth/2fa/setup/page.tsx
  function Setup2FAPage (line 16) | function Setup2FAPage() {

FILE: src/app/auth/delete-account/DeleteAccountClient.tsx
  type DeleteAccountClientProps (line 15) | type DeleteAccountClientProps = {
  function DeleteAccountClient (line 19) | function DeleteAccountClient({

FILE: src/app/auth/delete-account/page.tsx
  function DeleteAccountPage (line 11) | async function DeleteAccountPage() {

FILE: src/app/auth/idp/[idpId]/oidc/callback/page.tsx
  function Page (line 14) | async function Page(props: {

FILE: src/app/auth/initial-setup/layout.tsx
  function Layout (line 7) | async function Layout(props: { children: React.ReactNode }) {

FILE: src/app/auth/initial-setup/page.tsx
  function InitialSetupPage (line 45) | function InitialSetupPage() {

FILE: src/app/auth/layout.tsx
  type AuthLayoutProps (line 17) | type AuthLayoutProps = {
  function AuthLayout (line 21) | async function AuthLayout({ children }: AuthLayoutProps) {

FILE: src/app/auth/login/device/page.tsx
  type Props (line 10) | type Props = {
  function deviceRedirectSearchParams (line 14) | function deviceRedirectSearchParams(params: {
  function DeviceLoginPage (line 25) | async function DeviceLoginPage({ searchParams }: Props) {

FILE: src/app/auth/login/device/success/page.tsx
  function DeviceAuthSuccessPage (line 12) | function DeviceAuthSuccessPage() {

FILE: src/app/auth/login/page.tsx
  function Page (line 23) | async function Page(props: {
  function buildQueryString (line 205) | function buildQueryString(searchParams: {

FILE: src/app/auth/org/[orgId]/page.tsx
  function OrgAuthPage (line 18) | async function OrgAuthPage(props: {

FILE: src/app/auth/org/page.tsx
  function OrgAuthPage (line 24) | async function OrgAuthPage(props: {

FILE: src/app/auth/reset-password/ResetPasswordForm.tsx
  type ResetPasswordFormProps (line 53) | type ResetPasswordFormProps = {
  function ResetPasswordForm (line 60) | function ResetPasswordForm({

FILE: src/app/auth/reset-password/page.tsx
  function Page (line 13) | async function Page(props: {

FILE: src/app/auth/resource/[resourceGuid]/page.tsx
  function ResourceAuthPage (line 33) | async function ResourceAuthPage(props: {

FILE: src/app/auth/signup/page.tsx
  function Page (line 13) | async function Page(props: {

FILE: src/app/auth/verify-email/page.tsx
  function Page (line 10) | async function Page(props: {

FILE: src/app/invite/page.tsx
  function InvitePage (line 6) | async function InvitePage(props: {

FILE: src/app/layout.tsx
  function RootLayout (line 41) | async function RootLayout({
  function loadBrandingColors (line 138) | function loadBrandingColors() {

FILE: src/app/maintenance-screen/page.tsx
  function MaintenanceScreen (line 22) | async function MaintenanceScreen() {

FILE: src/app/navigation.tsx
  type SidebarNavSection (line 30) | type SidebarNavSection = {
  type OrgNavSectionsOptions (line 36) | type OrgNavSectionsOptions = {

FILE: src/app/not-found.tsx
  function NotFound (line 3) | async function NotFound() {

FILE: src/app/page.tsx
  function Page (line 20) | async function Page(props: {

FILE: src/app/robots.ts
  function robots (line 3) | function robots(): MetadataRoute.Robots {

FILE: src/app/s/[accessToken]/page.tsx
  function ResourceAuthPage (line 3) | async function ResourceAuthPage(props: {

FILE: src/app/setup/layout.tsx
  function SetupLayout (line 21) | async function SetupLayout({

FILE: src/app/setup/page.tsx
  type Step (line 36) | type Step = "org" | "site" | "resources";
  function StepperForm (line 38) | function StepperForm() {
  function debounce (line 438) | function debounce<T extends (...args: any[]) => any>(

FILE: src/components/AccessPageHeaderAndNav.tsx
  type AccessPageHeaderAndNavProps (line 7) | interface AccessPageHeaderAndNavProps {
  function AccessPageHeaderAndNav (line 12) | function AccessPageHeaderAndNav({

FILE: src/components/AccessToken.tsx
  type AccessTokenProps (line 18) | type AccessTokenProps = {
  function AccessToken (line 23) | function AccessToken({ token, resourceId }: AccessTokenProps) {

FILE: src/components/AccessTokenUsage.tsx
  type AccessTokenSectionProps (line 20) | interface AccessTokenSectionProps {
  function AccessTokenSection (line 26) | function AccessTokenSection({

FILE: src/components/ActionBanner.tsx
  type ActionBannerProps (line 47) | type ActionBannerProps = {
  function ActionBanner (line 55) | function ActionBanner({

FILE: src/components/AdminIdpDataTable.tsx
  type DataTableProps (line 8) | interface DataTableProps<TData, TValue> {
  function IdpDataTable (line 15) | function IdpDataTable<TData, TValue>({

FILE: src/components/AdminIdpTable.tsx
  type IdpRow (line 26) | type IdpRow = {
  type Props (line 34) | type Props = {
  function IdpTable (line 38) | function IdpTable({ idps }: Props) {

FILE: src/components/AdminUsersDataTable.tsx
  type DataTableProps (line 7) | interface DataTableProps<TData, TValue> {
  function UsersDataTable (line 14) | function UsersDataTable<TData, TValue>({

FILE: src/components/AdminUsersTable.tsx
  type GlobalUserRow (line 36) | type GlobalUserRow = {
  type Props (line 49) | type Props = {
  type AdminGeneratePasswordResetCodeResponse (line 53) | type AdminGeneratePasswordResetCodeResponse = {
  function UsersTable (line 59) | function UsersTable({ users }: Props) {

FILE: src/components/ApiKeysDataTable.tsx
  type DataTableProps (line 32) | interface DataTableProps<TData, TValue> {
  function ApiKeysDataTable (line 40) | function ApiKeysDataTable<TData, TValue>({

FILE: src/components/ApiKeysTable.tsx
  type ApiKeyRow (line 25) | type ApiKeyRow = {
  type ApiKeyTableProps (line 32) | type ApiKeyTableProps = {
  function ApiKeysTable (line 36) | function ApiKeysTable({ apiKeys }: ApiKeyTableProps) {

FILE: src/components/ApplyInternalRedirect.tsx
  type ApplyInternalRedirectProps (line 7) | type ApplyInternalRedirectProps = {
  function ApplyInternalRedirect (line 11) | function ApplyInternalRedirect({

FILE: src/components/ApprovalFeed.tsx
  type ApprovalFeedProps (line 36) | type ApprovalFeedProps = {
  function ApprovalFeed (line 41) | function ApprovalFeed({
  type ApprovalRequestProps (line 184) | type ApprovalRequestProps = {
  function ApprovalRequest (line 190) | function ApprovalRequest({ approval, orgId, onSuccess }: ApprovalRequest...

FILE: src/components/ApprovalsEmptyState.tsx
  type ApprovalsEmptyStateProps (line 16) | type ApprovalsEmptyStateProps = {
  function ApprovalsEmptyState (line 20) | function ApprovalsEmptyState({ orgId }: ApprovalsEmptyStateProps) {

FILE: src/components/AuthPageBrandingForm.tsx
  type AuthPageCustomizationProps (line 41) | type AuthPageCustomizationProps = {
  function AuthPageBrandingForm (line 130) | function AuthPageBrandingForm({

FILE: src/components/AuthPageSettings.tsx
  type AuthPageSettingsProps (line 56) | interface AuthPageSettingsProps {
  type AuthPageSettingsRef (line 62) | interface AuthPageSettingsRef {
  function AuthPageSettings (line 67) | function AuthPageSettings({

FILE: src/components/AutoLoginHandler.tsx
  type AutoLoginHandlerProps (line 20) | type AutoLoginHandlerProps = {
  function AutoLoginHandler (line 27) | function AutoLoginHandler({

FILE: src/components/AutoProvisionConfigWidget.tsx
  type Role (line 26) | type Role = {
  type AutoProvisionConfigWidgetProps (line 31) | type AutoProvisionConfigWidgetProps<T extends FieldValues> = {
  function AutoProvisionConfigWidget (line 42) | function AutoProvisionConfigWidget<T extends FieldValues>({

FILE: src/components/BlueprintDetailsForm.tsx
  type CreateBlueprintFormProps (line 34) | type CreateBlueprintFormProps = {
  function BlueprintDetailsForm (line 38) | function BlueprintDetailsForm({

FILE: src/components/BlueprintsTable.tsx
  type BlueprintRow (line 21) | type BlueprintRow = ListBlueprintsResponse["blueprints"][number];
  type Props (line 23) | type Props = {
  function BlueprintsTable (line 28) | function BlueprintsTable({ blueprints, orgId }: Props) {

FILE: src/components/BrandingLogo.tsx
  type BrandingLogoProps (line 9) | type BrandingLogoProps = {
  function BrandingLogo (line 15) | function BrandingLogo(props: BrandingLogoProps) {

FILE: src/components/CertificateStatus.tsx
  type CertificateStatusProps (line 8) | type CertificateStatusProps = {
  function CertificateStatus (line 20) | function CertificateStatus({

FILE: src/components/ChangePasswordDialog.tsx
  type ChangePasswordDialogProps (line 18) | type ChangePasswordDialogProps = {
  function ChangePasswordDialog (line 23) | function ChangePasswordDialog({

FILE: src/components/ChangePasswordForm.tsx
  type ChangePasswordFormProps (line 62) | type ChangePasswordFormProps = {

FILE: src/components/ClientInfoCard.tsx
  type ClientInfoCardProps (line 14) | type ClientInfoCardProps = {};
  function SiteInfoCard (line 16) | function SiteInfoCard({}: ClientInfoCardProps) {

FILE: src/components/ClientResourcesTable.tsx
  type InternalResourceRow (line 42) | type InternalResourceRow = {
  type ClientResourcesTableProps (line 66) | type ClientResourcesTableProps = {
  function ClientResourcesTable (line 73) | function ClientResourcesTable({

FILE: src/components/ClientsDataTable.tsx
  type TabFilter (line 6) | type TabFilter = {
  type DataTableProps (line 12) | interface DataTableProps<TData, TValue> {
  function ClientsDataTable (line 25) | function ClientsDataTable<TData, TValue>({

FILE: src/components/ColumnFilter.tsx
  type FilterOption (line 20) | interface FilterOption {
  type ColumnFilterProps (line 25) | interface ColumnFilterProps {
  function ColumnFilter (line 35) | function ColumnFilter({

FILE: src/components/ColumnFilterButton.tsx
  type FilterOption (line 20) | interface FilterOption {
  type ColumnFilterButtonProps (line 25) | interface ColumnFilterButtonProps {
  function ColumnFilterButton (line 36) | function ColumnFilterButton({

FILE: src/components/ConfirmDeleteDialog.tsx
  type InviteUserFormProps (line 28) | type InviteUserFormProps = {
  function ConfirmDeleteDialog (line 39) | function ConfirmDeleteDialog({

FILE: src/components/ContainersSelector.tsx
  type ContainerSelectorProps (line 51) | interface ContainerSelectorProps {
  function getContainerHostname (line 676) | function getContainerHostname(container: Container): string {

FILE: src/components/CopyTextBox.tsx
  type CopyTextBoxProps (line 8) | type CopyTextBoxProps = {
  function CopyTextBox (line 15) | function CopyTextBox({

FILE: src/components/CopyToClipboard.tsx
  type CopyToClipboardProps (line 6) | type CopyToClipboardProps = {

FILE: src/components/CreateBlueprintForm.tsx
  type CreateBlueprintFormProps (line 36) | type CreateBlueprintFormProps = {
  function CreateBlueprintForm (line 40) | function CreateBlueprintForm({

FILE: src/components/CreateDomainForm.tsx
  function toPunycode (line 59) | function toPunycode(domain: string): string {
  function fromPunycode (line 68) | function fromPunycode(domain: string): string {
  function isValidDomainFormat (line 77) | function isValidDomainFormat(domain: string): boolean {
  type FormValues (line 112) | type FormValues = z.infer<typeof formSchema>;
  type CreateDomainFormProps (line 114) | type CreateDomainFormProps = {
  function CreateDomainForm (line 126) | function CreateDomainForm({

FILE: src/components/CreateInternalResourceDialog.tsx
  type Site (line 28) | type Site = ListSitesResponse["sites"][0];
  type CreateInternalResourceDialogProps (line 30) | type CreateInternalResourceDialogProps = {
  function CreateInternalResourceDialog (line 38) | function CreateInternalResourceDialog({

FILE: src/components/CreateRoleForm.tsx
  type CreateRoleFormProps (line 29) | type CreateRoleFormProps = {
  function CreateRoleForm (line 35) | function CreateRoleForm({

FILE: src/components/CreateShareLinkForm.tsx
  type FormProps (line 73) | type FormProps = {
  function CreateShareLinkForm (line 79) | function CreateShareLinkForm({

FILE: src/components/Credenza.tsx
  type BaseProps (line 28) | interface BaseProps {
  type RootCredenzaProps (line 32) | interface RootCredenzaProps extends BaseProps {
  type CredenzaProps (line 37) | interface CredenzaProps extends BaseProps {

FILE: src/components/CustomDomainInput.tsx
  type DomainOption (line 14) | interface DomainOption {
  type CustomDomainInputProps (line 19) | interface CustomDomainInputProps {
  function CustomDomainInput (line 27) | function CustomDomainInput({

FILE: src/components/DNSRecordTable.tsx
  type DNSRecordRow (line 11) | type DNSRecordRow = {
  type Props (line 19) | type Props = {
  function DNSRecordsTable (line 24) | function DNSRecordsTable({ records, type }: Props) {

FILE: src/components/DNSRecordsDataTable.tsx
  type TabFilter (line 34) | type TabFilter = {
  type DNSRecordsDataTableProps (line 40) | type DNSRecordsDataTableProps<TData, TValue> = {
  function DNSRecordsDataTable (line 61) | function DNSRecordsDataTable<TData, TValue>({

FILE: src/components/DashboardLoginForm.tsx
  type DashboardLoginFormProps (line 24) | type DashboardLoginFormProps = {
  function DashboardLoginForm (line 35) | function DashboardLoginForm({

FILE: src/components/DataTablePagination.tsx
  type DataTablePaginationProps (line 19) | interface DataTablePaginationProps<TData> {
  function DataTablePagination (line 31) | function DataTablePagination<TData>({

FILE: src/components/DateTimePicker.tsx
  type DateTimeValue (line 17) | interface DateTimeValue {
  type DateTimePickerProps (line 22) | interface DateTimePickerProps {
  function DateTimePicker (line 32) | function DateTimePicker({
  type DateRangePickerProps (line 167) | interface DateRangePickerProps {
  function DateRangePicker (line 180) | function DateRangePicker({

FILE: src/components/DeleteAccountConfirmDialog.tsx
  type DeleteAccountConfirmDialogProps (line 45) | type DeleteAccountConfirmDialogProps = {
  function DeleteAccountConfirmDialog (line 50) | function DeleteAccountConfirmDialog({

FILE: src/components/DeleteRoleForm.tsx
  type CreateRoleFormProps (line 43) | type CreateRoleFormProps = {
  function DeleteRoleForm (line 50) | function DeleteRoleForm({

FILE: src/components/DeviceAuthConfirmation.tsx
  type DeviceAuthMetadata (line 18) | type DeviceAuthMetadata = {
  type DeviceAuthConfirmationProps (line 26) | type DeviceAuthConfirmationProps = {
  function DeviceAuthConfirmation (line 33) | function DeviceAuthConfirmation({

FILE: src/components/DeviceLoginForm.tsx
  type DeviceAuthMetadata (line 46) | type DeviceAuthMetadata = {
  type DeviceLoginFormProps (line 54) | type DeviceLoginFormProps = {
  function DeviceLoginForm (line 61) | function DeviceLoginForm({

FILE: src/components/Disable2FaForm.tsx
  type Disable2FaProps (line 37) | type Disable2FaProps = {
  function Disable2FaForm (line 42) | function Disable2FaForm({ open, setOpen }: Disable2FaProps) {

FILE: src/components/DismissableBanner.tsx
  type DismissableBannerProps (line 9) | type DismissableBannerProps = {

FILE: src/components/DomainCertForm.tsx
  type DomainInfoCardProps (line 45) | type DomainInfoCardProps = {
  function toPunycode (line 52) | function toPunycode(domain: string): string {
  function isValidDomainFormat (line 61) | function isValidDomainFormat(domain: string): boolean {
  type FormValues (line 96) | type FormValues = z.infer<typeof formSchema>;
  function DomainCertForm (line 103) | function DomainCertForm({

FILE: src/components/DomainInfoCard.tsx
  type DomainInfoCardProps (line 15) | type DomainInfoCardProps = {
  function DomainInfoCard (line 22) | function DomainInfoCard({

FILE: src/components/DomainPicker.tsx
  type AvailableOption (line 49) | type AvailableOption = {
  type DomainOption (line 55) | type DomainOption = {
  type DomainPickerProps (line 65) | interface DomainPickerProps {
  function DomainPicker (line 84) | function DomainPicker({
  function debounce (line 802) | function debounce<T extends (...args: any[]) => any>(

FILE: src/components/DomainsDataTable.tsx
  type DataTableProps (line 7) | interface DataTableProps<TData, TValue> {
  function DomainsDataTable (line 15) | function DomainsDataTable<TData, TValue>({

FILE: src/components/DomainsTable.tsx
  
Condensed preview — 1253 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (9,898K chars).
[
  {
    "path": ".dockerignore",
    "chars": 390,
    "preview": "/node_modules\n/.pnp\n.pnp.js\n.yarn/install-state.gz\n/coverage\n/.next/\n/out/\n/build\n.DS_Store\n*.pem\nnpm-debug.log*\nyarn-de"
  },
  {
    "path": ".editorconfig",
    "chars": 221,
    "preview": "root = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 4\ninsert_final_newline = true\ntrim_trailing_whitespa"
  },
  {
    "path": ".eslintrc.json",
    "chars": 63,
    "preview": "{\n    \"extends\": [\"next/core-web-vitals\", \"next/typescript\"]\n}\n"
  },
  {
    "path": ".github/DISCUSSION_TEMPLATE/feature-requests.yml",
    "chars": 1540,
    "preview": "body:\n    - type: textarea\n      attributes:\n          label: Summary\n          description: A clear and concise summary"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 63,
    "preview": "# These are supported funding model platforms\n\ngithub: [fosrl]\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/1.bug_report.yml",
    "chars": 1754,
    "preview": "name: Bug Report\ndescription: Create a bug report\nlabels: []\nbody:\n    - type: textarea\n      attributes:\n          labe"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 424,
    "preview": "blank_issues_enabled: false\ncontact_links:\n    - name: Need help or have questions?\n      url: https://github.com/orgs/f"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 411,
    "preview": "## Community Contribution License Agreement\nBy creating this pull request, I grant the project maintainers an unlimited,"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 1113,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"npm\"\n    directory: \"/\"\n    schedule:\n      interval: \"daily\"\n    groups:\n  "
  },
  {
    "path": ".github/workflows/cicd.yml",
    "chars": 25809,
    "preview": "name: Public CICD Pipeline\n\n# CI/CD workflow for building, publishing, mirroring, signing container images and building "
  },
  {
    "path": ".github/workflows/linting.yml",
    "chars": 923,
    "preview": "name: ESLint\n\npermissions:\n  contents: read\n\non:\n    pull_request:\n        paths:\n            - '**/*.js'\n            - "
  },
  {
    "path": ".github/workflows/mirror.yaml",
    "chars": 4701,
    "preview": "name: Mirror & Sign (Docker Hub to GHCR)\n\non:\n  workflow_dispatch: {}\n\npermissions:\n  contents: read\n  packages: write\n "
  },
  {
    "path": ".github/workflows/restart-runners.yml",
    "chars": 1168,
    "preview": "name: Restart Runners\n\non:\n  schedule:\n    - cron: '0 0 */7 * *'\n\npermissions:\n    id-token: write\n    contents: read\n\nj"
  },
  {
    "path": ".github/workflows/saas.yml",
    "chars": 6197,
    "preview": "name: SAAS Pipeline\n\n# CI/CD workflow for building, publishing, mirroring, signing container images and building release"
  },
  {
    "path": ".github/workflows/stale-bot.yml",
    "chars": 1371,
    "preview": "name: Mark and Close Stale Issues\n\non:\n  schedule:\n    - cron: '0 0 * * *'\n  workflow_dispatch: # Allow manual trigger\n\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 1845,
    "preview": "name: Run Tests\n\npermissions:\n  contents: read\n\non:\n  pull_request:\n    branches:\n      - main\n      - dev\n\njobs:\n  test"
  },
  {
    "path": ".gitignore",
    "chars": 725,
    "preview": "/node_modules\n/.pnp\n.pnp.js\n.yarn/install-state.gz\n/coverage\n/.next/\n/out/\n/build\n.DS_Store\n*.pem\nnpm-debug.log*\nyarn-de"
  },
  {
    "path": ".nvmrc",
    "chars": 3,
    "preview": "24\n"
  },
  {
    "path": ".prettierignore",
    "chars": 116,
    "preview": ".github/\nbruno/\ncli/\nconfig/\nmessages/\nnext.config.mjs/\npublic/\ntailwind.config.js/\ntest/\n**/*.yml\n**/*.yaml\n**/*.md"
  },
  {
    "path": ".prettierrc",
    "chars": 73,
    "preview": "{\n    \"tabWidth\": 4,\n    \"printWidth\": 80,\n    \"trailingComma\": \"none\"\n}\n"
  },
  {
    "path": ".vscode/extensions.json",
    "chars": 54,
    "preview": "{\n    \"recommendations\": [\"esbenp.prettier-vscode\"]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 627,
    "preview": "{\n    \"editor.codeActionsOnSave\": {\n        \"source.addMissingImports.ts\": \"always\"\n    },\n    \"editor.defaultFormatter\""
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 913,
    "preview": "## Contributing\n\nContributions are welcome! \n\nPlease see the contribution and local development guide on the docs page b"
  },
  {
    "path": "Dockerfile",
    "chars": 3032,
    "preview": "# FROM node:24-slim AS base\nFROM public.ecr.aws/docker/library/node:24-slim AS base\n\nWORKDIR /app\n\nRUN apt-get update &&"
  },
  {
    "path": "Dockerfile.dev",
    "chars": 237,
    "preview": "FROM node:24-alpine\n\nWORKDIR /app\n\nRUN apk add --no-cache python3 make g++\n\nCOPY package*.json ./\n\n# Install dependencie"
  },
  {
    "path": "LICENSE",
    "chars": 36027,
    "preview": "Copyright (c) 2025 Fossorial, Inc.\n\nPortions of this software are licensed as follows:\n\n* All files that include a heade"
  },
  {
    "path": "Makefile",
    "chars": 19722,
    "preview": ".PHONY: build build-pg build-release build-release-arm build-release-amd create-manifests build-arm build-x86 test clean"
  },
  {
    "path": "README.md",
    "chars": 7291,
    "preview": "<div align=\"center\">\n    <h2>\n    <a href=\"https://pangolin.net/\">\n        <picture>\n            <source media=\"(prefers"
  },
  {
    "path": "SECURITY.md",
    "chars": 721,
    "preview": "# Security Policy\n\nIf you discover a security vulnerability, please follow the steps below to responsibly disclose it to"
  },
  {
    "path": "bruno/API Keys/Create API Key.bru",
    "chars": 178,
    "preview": "meta {\n  name: Create API Key\n  type: http\n  seq: 1\n}\n\nput {\n  url: http://localhost:3000/api/v1/api-key\n  body: json\n  "
  },
  {
    "path": "bruno/API Keys/Delete API Key.bru",
    "chars": 155,
    "preview": "meta {\n  name: Delete API Key\n  type: http\n  seq: 2\n}\n\ndelete {\n  url: http://localhost:3000/api/v1/api-key/dm47aacqxxn3"
  },
  {
    "path": "bruno/API Keys/List API Key Actions.bru",
    "chars": 166,
    "preview": "meta {\n  name: List API Key Actions\n  type: http\n  seq: 6\n}\n\nget {\n  url: http://localhost:3000/api/v1/api-key/ex0izu2c3"
  },
  {
    "path": "bruno/API Keys/List Org API Keys.bru",
    "chars": 153,
    "preview": "meta {\n  name: List Org API Keys\n  type: http\n  seq: 4\n}\n\nget {\n  url: http://localhost:3000/api/v1/org/home-lab/api-key"
  },
  {
    "path": "bruno/API Keys/List Root API Keys.bru",
    "chars": 146,
    "preview": "meta {\n  name: List Root API Keys\n  type: http\n  seq: 3\n}\n\nget {\n  url: http://localhost:3000/api/v1/root/api-keys\n  bod"
  },
  {
    "path": "bruno/API Keys/Set API Key Actions.bru",
    "chars": 220,
    "preview": "meta {\n  name: Set API Key Actions\n  type: http\n  seq: 5\n}\n\npost {\n  url: http://localhost:3000/api/v1/api-key/ex0izu2c3"
  },
  {
    "path": "bruno/API Keys/Set API Key Orgs.bru",
    "chars": 210,
    "preview": "meta {\n  name: Set API Key Orgs\n  type: http\n  seq: 7\n}\n\npost {\n  url: http://localhost:3000/api/v1/api-key/ex0izu2c37fj"
  },
  {
    "path": "bruno/API Keys/folder.bru",
    "chars": 26,
    "preview": "meta {\n  name: API Keys\n}\n"
  },
  {
    "path": "bruno/Auth/2fa-disable.bru",
    "chars": 216,
    "preview": "meta {\n  name: 2fa-disable\n  type: http\n  seq: 6\n}\n\npost {\n  url: http://localhost:3000/api/v1/auth/2fa/disable\n  body: "
  },
  {
    "path": "bruno/Auth/2fa-enable.bru",
    "chars": 184,
    "preview": "meta {\n  name: 2fa-enable\n  type: http\n  seq: 4\n}\n\npost {\n  url: http://localhost:3000/api/v1/auth/2fa/enable\n  body: js"
  },
  {
    "path": "bruno/Auth/2fa-request.bru",
    "chars": 192,
    "preview": "meta {\n  name: 2fa-request\n  type: http\n  seq: 5\n}\n\npost {\n  url: http://localhost:3000/api/v1/auth/2fa/request\n  body: "
  },
  {
    "path": "bruno/Auth/change-password.bru",
    "chars": 220,
    "preview": "meta {\n  name: change-password\n  type: http\n  seq: 9\n}\n\npost {\n  url: http://localhost:3000/api/v1/auth/change-password\n"
  },
  {
    "path": "bruno/Auth/login.bru",
    "chars": 213,
    "preview": "meta {\n  name: login\n  type: http\n  seq: 1\n}\n\npost {\n  url: http://localhost:3000/api/v1/auth/login\n  body: json\n  auth:"
  },
  {
    "path": "bruno/Auth/logout.bru",
    "chars": 130,
    "preview": "meta {\n  name: logout\n  type: http\n  seq: 3\n}\n\npost {\n  url: http://localhost:4000/api/v1/auth/logout\n  body: none\n  aut"
  },
  {
    "path": "bruno/Auth/reset-password-request.bru",
    "chars": 221,
    "preview": "meta {\n  name: reset-password-request\n  type: http\n  seq: 10\n}\n\npost {\n  url: http://localhost:3000/api/v1/auth/reset-pa"
  },
  {
    "path": "bruno/Auth/reset-password.bru",
    "chars": 287,
    "preview": "meta {\n  name: reset-password\n  type: http\n  seq: 11\n}\n\npost {\n  url: http://localhost:3000/api/v1/auth/reset-password\n "
  },
  {
    "path": "bruno/Auth/signup.bru",
    "chars": 219,
    "preview": "meta {\n  name: signup\n  type: http\n  seq: 2\n}\n\nput {\n  url: http://localhost:3000/api/v1/auth/signup\n  body: json\n  auth"
  },
  {
    "path": "bruno/Auth/verify-email-request.bru",
    "chars": 158,
    "preview": "meta {\n  name: verify-email-request\n  type: http\n  seq: 8\n}\n\npost {\n  url: http://localhost:3000/api/v1/auth/verify-emai"
  },
  {
    "path": "bruno/Auth/verify-email.bru",
    "chars": 190,
    "preview": "meta {\n  name: verify-email\n  type: http\n  seq: 7\n}\n\npost {\n  url: http://localhost:3000/api/v1/auth/verify-email\n  body"
  },
  {
    "path": "bruno/Auth/verify-user.bru",
    "chars": 264,
    "preview": "meta {\n  name: verify-user\n  type: http\n  seq: 4\n}\n\nget {\n  url: http://localhost:3001/api/v1/badger/verify-user?session"
  },
  {
    "path": "bruno/Clients/createClient.bru",
    "chars": 358,
    "preview": "meta {\n  name: createClient\n  type: http\n  seq: 1\n}\n\nput {\n  url: http://localhost:3000/api/v1/site/1/client\n  body: jso"
  },
  {
    "path": "bruno/Clients/pickClientDefaults.bru",
    "chars": 157,
    "preview": "meta {\n  name: pickClientDefaults\n  type: http\n  seq: 2\n}\n\nget {\n  url: http://localhost:3000/api/v1/site/1/pick-client-"
  },
  {
    "path": "bruno/IDP/Create OIDC Provider.bru",
    "chars": 594,
    "preview": "meta {\n  name: Create OIDC Provider\n  type: http\n  seq: 1\n}\n\nput {\n  url: http://localhost:3000/api/v1/org/home-lab/idp/"
  },
  {
    "path": "bruno/IDP/Generate OIDC URL.bru",
    "chars": 131,
    "preview": "meta {\n  name: Generate OIDC URL\n  type: http\n  seq: 2\n}\n\nget {\n  url: http://localhost:3000/api/v1\n  body: none\n  auth:"
  },
  {
    "path": "bruno/IDP/folder.bru",
    "chars": 21,
    "preview": "meta {\n  name: IDP\n}\n"
  },
  {
    "path": "bruno/Internal/Traefik Config.bru",
    "chars": 143,
    "preview": "meta {\n  name: Traefik Config\n  type: http\n  seq: 1\n}\n\nget {\n  url: http://localhost:3001/api/v1/traefik-config\n  body: "
  },
  {
    "path": "bruno/Internal/folder.bru",
    "chars": 26,
    "preview": "meta {\n  name: Internal\n}\n"
  },
  {
    "path": "bruno/Newt/Create Newt.bru",
    "chars": 127,
    "preview": "meta {\n  name: Create Newt\n  type: http\n  seq: 2\n}\n\nget {\n  url: http://localhost:3000/api/v1/newt\n  body: none\n  auth: "
  },
  {
    "path": "bruno/Newt/Get Token.bru",
    "chars": 260,
    "preview": "meta {\n  name: Get Token\n  type: http\n  seq: 1\n}\n\nget {\n  url: http://localhost:3000/api/v1/auth/newt/get-token\n  body: "
  },
  {
    "path": "bruno/Olm/createOlm.bru",
    "chars": 159,
    "preview": "meta {\n  name: createOlm\n  type: http\n  seq: 1\n}\n\nput {\n  url: http://localhost:3000/api/v1/olm\n  body: none\n  auth: inh"
  },
  {
    "path": "bruno/Olm/folder.bru",
    "chars": 57,
    "preview": "meta {\n  name: Olm\n  seq: 15\n}\n\nauth {\n  mode: inherit\n}\n"
  },
  {
    "path": "bruno/Orgs/Check Id.bru",
    "chars": 131,
    "preview": "meta {\n  name: Check Id\n  type: http\n  seq: 2\n}\n\nget {\n  url: http://localhost:3000/api/v1/org/checkId\n  body: none\n  au"
  },
  {
    "path": "bruno/Orgs/listOrgs.bru",
    "chars": 91,
    "preview": "meta {\n  name: listOrgs\n  type: http\n  seq: 1\n}\n\nget {\n  url: \n  body: none\n  auth: none\n}\n"
  },
  {
    "path": "bruno/Remote Exit Node/createRemoteExitNode.bru",
    "chars": 172,
    "preview": "meta {\n  name: createRemoteExitNode\n  type: http\n  seq: 1\n}\n\nput {\n  url: http://localhost:4000/api/v1/org/org_i21aifypn"
  },
  {
    "path": "bruno/Resources/listResourcesByOrg.bru",
    "chars": 101,
    "preview": "meta {\n  name: listResourcesByOrg\n  type: http\n  seq: 1\n}\n\nget {\n  url: \n  body: none\n  auth: none\n}\n"
  },
  {
    "path": "bruno/Resources/listResourcesBySite.bru",
    "chars": 207,
    "preview": "meta {\n  name: listResourcesBySite\n  type: http\n  seq: 2\n}\n\nget {\n  url: http://localhost:3000/api/v1/site/1/resources?l"
  },
  {
    "path": "bruno/Sites/Get Site.bru",
    "chars": 160,
    "preview": "meta {\n  name: Get Site\n  type: http\n  seq: 2\n}\n\nget {\n  url: http://localhost:3000/api/v1/org/test/sites/mexican-mole-l"
  },
  {
    "path": "bruno/Sites/listSites.bru",
    "chars": 92,
    "preview": "meta {\n  name: listSites\n  type: http\n  seq: 1\n}\n\nget {\n  url: \n  body: none\n  auth: none\n}\n"
  },
  {
    "path": "bruno/Targets/listTargets.bru",
    "chars": 218,
    "preview": "meta {\n  name: listTargets\n  type: http\n  seq: 1\n}\n\nget {\n  url: http://localhost:3000/api/v1/resource/web.main.localhos"
  },
  {
    "path": "bruno/Test.bru",
    "chars": 118,
    "preview": "meta {\n  name: Test\n  type: http\n  seq: 2\n}\n\nget {\n  url: http://localhost:3000/api/v1\n  body: none\n  auth: inherit\n}\n"
  },
  {
    "path": "bruno/Traefik/traefik-config.bru",
    "chars": 140,
    "preview": "meta {\n  name: traefik-config\n  type: http\n  seq: 1\n}\n\nget {\n  url: http://localhost:3001/api/v1/traefik-config\n  body: "
  },
  {
    "path": "bruno/Users/adminListUsers.bru",
    "chars": 131,
    "preview": "meta {\n  name: adminListUsers\n  type: http\n  seq: 2\n}\n\nget {\n  url: http://localhost:3000/api/v1/users\n  body: none\n  au"
  },
  {
    "path": "bruno/Users/adminRemoveUser.bru",
    "chars": 150,
    "preview": "meta {\n  name: adminRemoveUser\n  type: http\n  seq: 3\n}\n\ndelete {\n  url: http://localhost:3000/api/v1/user/ky5r7ivqs8wc7u"
  },
  {
    "path": "bruno/Users/getUser.bru",
    "chars": 90,
    "preview": "meta {\n  name: getUser\n  type: http\n  seq: 1\n}\n\nget {\n  url: \n  body: none\n  auth: none\n}\n"
  },
  {
    "path": "bruno/bruno.json",
    "chars": 212,
    "preview": "{\n  \"version\": \"1\",\n  \"name\": \"Pangolin\",\n  \"type\": \"collection\",\n  \"ignore\": [\n    \"node_modules\",\n    \".git\"\n  ],\n  \"p"
  },
  {
    "path": "cli/commands/clearExitNodes.ts",
    "chars": 973,
    "preview": "import { CommandModule } from \"yargs\";\nimport { db, exitNodes } from \"@server/db\";\nimport { eq } from \"drizzle-orm\";\n\nty"
  },
  {
    "path": "cli/commands/clearLicenseKeys.ts",
    "chars": 997,
    "preview": "import { CommandModule } from \"yargs\";\nimport { db, licenseKey } from \"@server/db\";\nimport { eq } from \"drizzle-orm\";\n\nt"
  },
  {
    "path": "cli/commands/deleteClient.ts",
    "chars": 5072,
    "preview": "import { CommandModule } from \"yargs\";\nimport { db, clients, olms, currentFingerprint, userClients, approvals } from \"@s"
  },
  {
    "path": "cli/commands/generateOrgCaKeys.ts",
    "chars": 4297,
    "preview": "import { CommandModule } from \"yargs\";\nimport { db, orgs } from \"@server/db\";\nimport { eq } from \"drizzle-orm\";\nimport {"
  },
  {
    "path": "cli/commands/resetUserSecurityKeys.ts",
    "chars": 2217,
    "preview": "import { CommandModule } from \"yargs\";\nimport { db, users, securityKeys } from \"@server/db\";\nimport { eq } from \"drizzle"
  },
  {
    "path": "cli/commands/rotateServerSecret.ts",
    "chars": 11264,
    "preview": "import { CommandModule } from \"yargs\";\nimport { db, idpOidcConfig, licenseKey } from \"@server/db\";\nimport { encrypt, dec"
  },
  {
    "path": "cli/commands/setAdminCredentials.ts",
    "chars": 5145,
    "preview": "import { CommandModule } from \"yargs\";\nimport { hashPassword, verifyPassword } from \"@server/auth/password\";\nimport { db"
  },
  {
    "path": "cli/index.ts",
    "chars": 871,
    "preview": "#!/usr/bin/env node\n\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { setAdminCredentials } "
  },
  {
    "path": "cli/wrapper.sh",
    "chars": 39,
    "preview": "#!/bin/sh\ncd /app/\n./dist/cli.mjs \"$@\"\n"
  },
  {
    "path": "components.json",
    "chars": 477,
    "preview": "{\n    \"$schema\": \"https://ui.shadcn.com/schema.json\",\n    \"style\": \"default\",\n    \"rsc\": true,\n    \"tsx\": true,\n    \"tai"
  },
  {
    "path": "config/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "config/config.example.yml",
    "chars": 732,
    "preview": "# To see all available options, please visit the docs:\n# https://docs.pangolin.net/\n\ngerbil:\n    start_port: 51820\n    b"
  },
  {
    "path": "config/db/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "config/logs/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "config/traefik/dynamic_config.yml",
    "chars": 1386,
    "preview": "http:\n  middlewares:\n    badger:\n      plugin:\n        badger:\n          disableForwardAuth: true\n    redirect-to-https:"
  },
  {
    "path": "config/traefik/traefik_config.yml",
    "chars": 1020,
    "preview": "api:\n  insecure: true\n  dashboard: true\n\nproviders:\n  http:\n    endpoint: \"http://pangolin:3001/api/v1/traefik-config\"\n "
  },
  {
    "path": "crowdin.yml",
    "chars": 80,
    "preview": "files:\n  - source: /messages/en-US.json\n    translation: /messages/%locale%.json"
  },
  {
    "path": "docker-compose.drizzle.yml",
    "chars": 325,
    "preview": "services:\n  drizzle-gateway:\n    image: ghcr.io/drizzle-team/gateway:latest\n    ports:\n      - \"4984:4983\"\n    depends_o"
  },
  {
    "path": "docker-compose.example.yml",
    "chars": 1582,
    "preview": "name: pangolin\nservices:\n  pangolin:\n    image: fosrl/pangolin:latest\n    container_name: pangolin\n    restart: unless-s"
  },
  {
    "path": "docker-compose.pgr.yml",
    "chars": 721,
    "preview": "services:\n  # PostgreSQL Service\n  db:\n    image: postgres:17 # Use the PostgreSQL 17 image\n    container_name: dev_post"
  },
  {
    "path": "docker-compose.yml",
    "chars": 813,
    "preview": "services:\n  # Development application service\n  app:\n    build:\n      context: .\n      dockerfile: Dockerfile.dev\n    co"
  },
  {
    "path": "drizzle.pg.config.ts",
    "chars": 350,
    "preview": "import { defineConfig } from \"drizzle-kit\";\nimport path from \"path\";\n\nconst schema = [path.join(\"server\", \"db\", \"pg\", \"s"
  },
  {
    "path": "drizzle.sqlite.config.ts",
    "chars": 401,
    "preview": "import { APP_PATH } from \"@server/lib/consts\";\nimport { defineConfig } from \"drizzle-kit\";\nimport path from \"path\";\n\ncon"
  },
  {
    "path": "esbuild.mjs",
    "chars": 11153,
    "preview": "import esbuild from \"esbuild\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { nodeExternal"
  },
  {
    "path": "eslint.config.js",
    "chars": 425,
    "preview": "import tseslint from \"typescript-eslint\";\n\nexport default tseslint.config({\n    files: [\"**/*.{ts,tsx,js,jsx}\"],\n    lan"
  },
  {
    "path": "install/Makefile",
    "chars": 1164,
    "preview": "all: go-build-release\n\n# Build with version injection via ldflags\n# Versions can be passed via: make go-build-release PA"
  },
  {
    "path": "install/config/config.yml",
    "chars": 1039,
    "preview": "# To see all available options, please visit the docs:\n# https://docs.pangolin.net/\n\ngerbil:\n    start_port: 51820\n    b"
  },
  {
    "path": "install/config/crowdsec/acquis.d/appsec.yaml",
    "chars": 132,
    "preview": "listen_addr: 0.0.0.0:7422\nappsec_config: crowdsecurity/appsec-default\nname: myAppSecComponent\nsource: appsec\nlabels:\n  t"
  },
  {
    "path": "install/config/crowdsec/acquis.d/traefik.yaml",
    "chars": 90,
    "preview": "poll_without_inotify: false\nfilenames:\n  - /var/log/traefik/*.log\nlabels:\n  type: traefik\n"
  },
  {
    "path": "install/config/crowdsec/docker-compose.yml",
    "chars": 1058,
    "preview": "services:\n  crowdsec:\n    image: docker.io/crowdsecurity/crowdsec:latest\n    container_name: crowdsec\n    environment:\n "
  },
  {
    "path": "install/config/crowdsec/dynamic_config.yml",
    "chars": 4553,
    "preview": "http:\n  middlewares:\n    badger:\n      plugin:\n        badger:\n          disableForwardAuth: true\n    redirect-to-https:"
  },
  {
    "path": "install/config/crowdsec/profiles.yaml",
    "chars": 518,
    "preview": "name: captcha_remediation\nfilters:\n  - Alert.Remediation == true && Alert.GetScope() == \"Ip\" && Alert.GetScenario() cont"
  },
  {
    "path": "install/config/crowdsec/traefik_config.yml",
    "chars": 2900,
    "preview": "api:\n  insecure: true\n  dashboard: true\n\nproviders:\n  http:\n    endpoint: \"http://pangolin:3001/api/v1/traefik-config\"\n "
  },
  {
    "path": "install/config/docker-compose.yml",
    "chars": 1809,
    "preview": "name: pangolin\nservices:\n  pangolin:\n    image: docker.io/fosrl/pangolin:{{if .IsEnterprise}}ee-{{end}}{{.PangolinVersio"
  },
  {
    "path": "install/config/traefik/dynamic_config.yml",
    "chars": 1615,
    "preview": "http:\n  middlewares:\n    badger:\n      plugin:\n        badger:\n          disableForwardAuth: true\n    redirect-to-https:"
  },
  {
    "path": "install/config/traefik/traefik_config.yml",
    "chars": 1020,
    "preview": "api:\n  insecure: true\n  dashboard: true\n\nproviders:\n  http:\n    endpoint: \"http://pangolin:3001/api/v1/traefik-config\"\n "
  },
  {
    "path": "install/config.go",
    "chars": 9543,
    "preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\n\t\"gopkg.in/yaml.v3\"\n)\n\n// TraefikConfig represents t"
  },
  {
    "path": "install/containers.go",
    "chars": 11477,
    "preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"os/user\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc wait"
  },
  {
    "path": "install/crowdsec.go",
    "chars": 6124,
    "preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\n\t\"gopkg.in/yaml.v3\"\n)\n\nfunc installCrowdsec(c"
  },
  {
    "path": "install/get-installer.sh",
    "chars": 4812,
    "preview": "#!/bin/bash\n\n# Get installer - Cross-platform installation script\n# Usage: curl -fsSL https://raw.githubusercontent.com/"
  },
  {
    "path": "install/go.mod",
    "chars": 1601,
    "preview": "module installer\n\ngo 1.25.0\n\nrequire (\n\tgithub.com/charmbracelet/huh v0.8.0\n\tgithub.com/charmbracelet/lipgloss v1.1.0\n\tg"
  },
  {
    "path": "install/go.sum",
    "chars": 7678,
    "preview": "github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=\ngithub.com/MakeNowJust/heredoc v1."
  },
  {
    "path": "install/input.go",
    "chars": 4750,
    "preview": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\n\t\"github.com/charmbracelet/huh\"\n\t\"golang.org/x/term\"\n)\n\n// pan"
  },
  {
    "path": "install/input.txt",
    "chars": 127,
    "preview": "docker\nexample.com\npangolin.example.com\nyes\nadmin@example.com\nyes\nadmin@example.com\nPassword123!\nPassword123!\nyes\nno\nno\n"
  },
  {
    "path": "install/main.go",
    "chars": 22961,
    "preview": "package main\n\nimport (\n\t\"crypto/rand\"\n\t\"embed\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\""
  },
  {
    "path": "install/theme.go",
    "chars": 2456,
    "preview": "package main\n\nimport (\n\t\"github.com/charmbracelet/huh\"\n\t\"github.com/charmbracelet/lipgloss\"\n)\n\n// Pangolin brand colors "
  },
  {
    "path": "messages/bg-BG.json",
    "chars": 182918,
    "preview": "{\n    \"setupCreate\": \"Създайте организацията, сайта и ресурсите\",\n    \"headerAuthCompatibilityInfo\": \"Активирайте това, "
  },
  {
    "path": "messages/cs-CZ.json",
    "chars": 171121,
    "preview": "{\n    \"setupCreate\": \"Vytvořte organizaci, stránku a zdroje\",\n    \"headerAuthCompatibilityInfo\": \"Povolte toto, aby vyvo"
  },
  {
    "path": "messages/de-DE.json",
    "chars": 186195,
    "preview": "{\n    \"setupCreate\": \"Organisation, Standort und Ressourcen erstellen\",\n    \"headerAuthCompatibilityInfo\": \"Aktivieren S"
  },
  {
    "path": "messages/en-US.json",
    "chars": 167603,
    "preview": "{\n    \"setupCreate\": \"Create the organization, site, and resources\",\n    \"headerAuthCompatibilityInfo\": \"Enable this to "
  },
  {
    "path": "messages/es-ES.json",
    "chars": 182964,
    "preview": "{\n    \"setupCreate\": \"Crear la organización, el sitio y los recursos\",\n    \"headerAuthCompatibilityInfo\": \"Habilite esto"
  },
  {
    "path": "messages/fr-FR.json",
    "chars": 190290,
    "preview": "{\n    \"setupCreate\": \"Créer l'organisation, le site et les ressources\",\n    \"headerAuthCompatibilityInfo\": \"Activez ceci"
  },
  {
    "path": "messages/it-IT.json",
    "chars": 182833,
    "preview": "{\n    \"setupCreate\": \"Creare l'organizzazione, il sito e le risorse\",\n    \"headerAuthCompatibilityInfo\": \"Abilita questo"
  },
  {
    "path": "messages/ko-KR.json",
    "chars": 127666,
    "preview": "{\n    \"setupCreate\": \"조직, 사이트 및 리소스를 생성합니다.\",\n    \"headerAuthCompatibilityInfo\": \"인증 토큰이 없을 때 401 Unauthorized 응답을 강제하도록"
  },
  {
    "path": "messages/nb-NO.json",
    "chars": 173174,
    "preview": "{\n    \"setupCreate\": \"Opprett organisasjonen, nettstedet og ressursene\",\n    \"headerAuthCompatibilityInfo\": \"Aktiver det"
  },
  {
    "path": "messages/nl-NL.json",
    "chars": 179265,
    "preview": "{\n    \"setupCreate\": \"Maak de organisatie, site en bronnen aan\",\n    \"headerAuthCompatibilityInfo\": \"Schakel dit in om e"
  },
  {
    "path": "messages/pl-PL.json",
    "chars": 177298,
    "preview": "{\n    \"setupCreate\": \"Utwórz organizację, witrynę i zasoby\",\n    \"headerAuthCompatibilityInfo\": \"Włącz to, aby wymusić o"
  },
  {
    "path": "messages/pt-PT.json",
    "chars": 178546,
    "preview": "{\n    \"setupCreate\": \"Criar a organização, o site e os recursos\",\n    \"headerAuthCompatibilityInfo\": \"Habilite isso para"
  },
  {
    "path": "messages/ru-RU.json",
    "chars": 177346,
    "preview": "{\n    \"setupCreate\": \"Создать организацию, сайт и ресурсы\",\n    \"headerAuthCompatibilityInfo\": \"Включите это, чтобы прин"
  },
  {
    "path": "messages/tr-TR.json",
    "chars": 174377,
    "preview": "{\n    \"setupCreate\": \"Organizasyonu, siteyi ve kaynakları oluşturun\",\n    \"headerAuthCompatibilityInfo\": \"Kimlik doğrula"
  },
  {
    "path": "messages/zh-CN.json",
    "chars": 111421,
    "preview": "{\n    \"setupCreate\": \"创建组织、站点和资源\",\n    \"headerAuthCompatibilityInfo\": \"启用此功能以在身份验证令牌缺失时强制返回401未授权响应。对于不在没有服务器挑战的情况下不发送凭证"
  },
  {
    "path": "messages/zh-TW.json",
    "chars": 94009,
    "preview": "{\n  \"setupCreate\": \"創建您的第一個組織、網站和資源\",\n  \"headerAuthCompatibilityInfo\": \"啟用此選項以在缺少驗證令牌時強制回傳 401 未授權回應。這對於不會在沒有伺服器挑戰的情況下發送"
  },
  {
    "path": "next.config.ts",
    "chars": 380,
    "preview": "import type { NextConfig } from \"next\";\nimport createNextIntlPlugin from \"next-intl/plugin\";\n\nconst withNextIntl = creat"
  },
  {
    "path": "package.json",
    "chars": 7666,
    "preview": "{\n    \"name\": \"@fosrl/pangolin\",\n    \"version\": \"0.0.0\",\n    \"private\": true,\n    \"type\": \"module\",\n    \"description\": \""
  },
  {
    "path": "postcss.config.mjs",
    "chars": 152,
    "preview": "/** @type {import('postcss-load-config').Config} */\nconst config = {\n    plugins: {\n        \"@tailwindcss/postcss\": {}\n "
  },
  {
    "path": "server/apiServer.ts",
    "chars": 4653,
    "preview": "import express from \"express\";\nimport cors from \"cors\";\nimport cookieParser from \"cookie-parser\";\nimport config from \"@s"
  },
  {
    "path": "server/auth/actions.ts",
    "chars": 7594,
    "preview": "import { Request } from \"express\";\nimport { db } from \"@server/db\";\nimport { userActions, roleActions, userOrgs } from \""
  },
  {
    "path": "server/auth/canUserAccessResource.ts",
    "chars": 1009,
    "preview": "import { db } from \"@server/db\";\nimport { and, eq } from \"drizzle-orm\";\nimport { roleResources, userResources } from \"@s"
  },
  {
    "path": "server/auth/canUserAccessSiteResource.ts",
    "chars": 1053,
    "preview": "import { db } from \"@server/db\";\nimport { and, eq } from \"drizzle-orm\";\nimport { roleSiteResources, userSiteResources } "
  },
  {
    "path": "server/auth/checkValidInvite.ts",
    "chars": 1063,
    "preview": "import { db } from \"@server/db\";\nimport { UserInvite, userInvites } from \"@server/db\";\nimport { isWithinExpirationDate }"
  },
  {
    "path": "server/auth/password.ts",
    "chars": 586,
    "preview": "import { hash, verify } from \"@node-rs/argon2\";\n\nexport async function verifyPassword(\n    password: string,\n    hash: s"
  },
  {
    "path": "server/auth/passwordSchema.ts",
    "chars": 554,
    "preview": "import z from \"zod\";\n\nexport const passwordSchema = z\n    .string()\n    .min(8, { message: \"Password must be at least 8 "
  },
  {
    "path": "server/auth/resourceOtp.ts",
    "chars": 2410,
    "preview": "import { db } from \"@server/db\";\nimport { resourceOtp } from \"@server/db\";\nimport { and, eq } from \"drizzle-orm\";\nimport"
  },
  {
    "path": "server/auth/sendEmailVerificationCode.ts",
    "chars": 1474,
    "preview": "import { TimeSpan, createDate } from \"oslo\";\nimport { generateRandomString, alphabet } from \"oslo/crypto\";\nimport { db }"
  },
  {
    "path": "server/auth/sessions/app.ts",
    "chars": 5590,
    "preview": "import {\n    encodeBase32LowerCaseNoPadding,\n    encodeHexLowerCase\n} from \"@oslojs/encoding\";\nimport { sha256 } from \"@"
  },
  {
    "path": "server/auth/sessions/newt.ts",
    "chars": 2282,
    "preview": "import { encodeHexLowerCase } from \"@oslojs/encoding\";\nimport { sha256 } from \"@oslojs/crypto/sha2\";\nimport { Newt, newt"
  },
  {
    "path": "server/auth/sessions/olm.ts",
    "chars": 2240,
    "preview": "import { encodeHexLowerCase } from \"@oslojs/encoding\";\nimport { sha256 } from \"@oslojs/crypto/sha2\";\nimport { Olm, olms,"
  },
  {
    "path": "server/auth/sessions/resource.ts",
    "chars": 6334,
    "preview": "import { encodeHexLowerCase } from \"@oslojs/encoding\";\nimport { sha256 } from \"@oslojs/crypto/sha2\";\nimport { resourceSe"
  },
  {
    "path": "server/auth/sessions/verifySession.ts",
    "chars": 931,
    "preview": "import { Request } from \"express\";\nimport {\n    validateSessionToken,\n    SESSION_COOKIE_NAME\n} from \"@server/auth/sessi"
  },
  {
    "path": "server/auth/totp.ts",
    "chars": 1492,
    "preview": "import { verify } from \"@node-rs/argon2\";\nimport { db } from \"@server/db\";\nimport { twoFactorBackupCodes } from \"@server"
  },
  {
    "path": "server/auth/unauthorizedResponse.ts",
    "chars": 212,
    "preview": "import HttpCode from \"@server/types/HttpCode\";\nimport createHttpError from \"http-errors\";\n\nexport function unauthorized("
  },
  {
    "path": "server/auth/verifyResourceAccessToken.ts",
    "chars": 3291,
    "preview": "import { db } from \"@server/db\";\nimport {\n    Resource,\n    ResourceAccessToken,\n    resourceAccessToken,\n    resources\n"
  },
  {
    "path": "server/cleanup.ts",
    "chars": 535,
    "preview": "import { flushBandwidthToDb } from \"@server/routers/newt/handleReceiveBandwidthMessage\";\nimport { flushSiteBandwidthToDb"
  },
  {
    "path": "server/db/README.md",
    "chars": 1559,
    "preview": "# Database\n\nPangolin can use a Postgres or SQLite database to store its data.\n\n## Development\n\n### Postgres\n\nTo use Post"
  },
  {
    "path": "server/db/asns.ts",
    "chars": 5662,
    "preview": "// Curated list of major ASNs (Cloud Providers, CDNs, ISPs, etc.)\n// This is not exhaustive - there are 100,000+ ASNs gl"
  },
  {
    "path": "server/db/countries.ts",
    "chars": 15456,
    "preview": "export const COUNTRIES = [\n    {\n        name: \"ALL COUNTRIES\",\n        code: \"ALL\" // THIS IS AN INVALID CC SO IT WILL "
  },
  {
    "path": "server/db/ios_models.json",
    "chars": 4508,
    "preview": "{\n  \"iPad1,1\": \"iPad\",\n  \"iPad2,1\": \"iPad 2\",\n  \"iPad2,2\": \"iPad 2\",\n  \"iPad2,3\": \"iPad 2\",\n  \"iPad2,4\": \"iPad 2\",\n  \"iP"
  },
  {
    "path": "server/db/mac_models.json",
    "chars": 5826,
    "preview": "{\n  \"PowerMac4,4\": \"eMac\",\n  \"PowerMac6,4\": \"eMac\",\n  \"PowerBook2,1\": \"iBook\",\n  \"PowerBook2,2\": \"iBook\",\n  \"PowerBook4,"
  },
  {
    "path": "server/db/maxmind.ts",
    "chars": 387,
    "preview": "import maxmind, { CountryResponse, Reader } from \"maxmind\";\nimport config from \"@server/lib/config\";\n\nlet maxmindLookup:"
  },
  {
    "path": "server/db/maxmindAsn.ts",
    "chars": 389,
    "preview": "import maxmind, { AsnResponse, Reader } from \"maxmind\";\nimport config from \"@server/lib/config\";\n\nlet maxmindAsnLookup: "
  },
  {
    "path": "server/db/migrate.ts",
    "chars": 60,
    "preview": "import { runMigrations } from \"./\";\n\nawait runMigrations();\n"
  },
  {
    "path": "server/db/names.json",
    "chars": 36616,
    "preview": "{\n    \"descriptors\": [\n        \"abandoned\",\n        \"able\",\n        \"absolute\",\n        \"adorable\",\n        \"adventurous"
  },
  {
    "path": "server/db/names.ts",
    "chars": 5952,
    "preview": "import { join } from \"path\";\nimport { readFileSync } from \"fs\";\nimport { clients, db, resources, siteResources } from \"@"
  },
  {
    "path": "server/db/pg/driver.ts",
    "chars": 2855,
    "preview": "import { drizzle as DrizzlePostgres } from \"drizzle-orm/node-postgres\";\nimport { Pool } from \"pg\";\nimport { readConfigFi"
  },
  {
    "path": "server/db/pg/index.ts",
    "chars": 184,
    "preview": "export * from \"./driver\";\nexport * from \"./logsDriver\";\nexport * from \"./safeRead\";\nexport * from \"./schema/schema\";\nexp"
  },
  {
    "path": "server/db/pg/logsDriver.ts",
    "chars": 2907,
    "preview": "import { drizzle as DrizzlePostgres } from \"drizzle-orm/node-postgres\";\nimport { Pool } from \"pg\";\nimport { readConfigFi"
  },
  {
    "path": "server/db/pg/migrate.ts",
    "chars": 567,
    "preview": "import { migrate } from \"drizzle-orm/node-postgres/migrator\";\nimport { db } from \"./driver\";\nimport path from \"path\";\n\nc"
  },
  {
    "path": "server/db/pg/safeRead.ts",
    "chars": 747,
    "preview": "import { db, primaryDb } from \"./driver\";\n\n/**\n * Runs a read query with replica fallback for Postgres.\n * Executes the "
  },
  {
    "path": "server/db/pg/schema/privateSchema.ts",
    "chars": 13890,
    "preview": "import {\n    pgTable,\n    serial,\n    varchar,\n    boolean,\n    integer,\n    bigint,\n    real,\n    text,\n    index\n} fro"
  },
  {
    "path": "server/db/pg/schema/schema.ts",
    "chars": 41485,
    "preview": "import { randomUUID } from \"crypto\";\nimport { InferSelectModel } from \"drizzle-orm\";\nimport {\n    bigint,\n    boolean,\n "
  },
  {
    "path": "server/db/queries/verifySessionQueries.ts",
    "chars": 5070,
    "preview": "import { db, loginPage, LoginPage, loginPageOrg, Org, orgs, roles } from \"@server/db\";\nimport {\n    Resource,\n    Resour"
  },
  {
    "path": "server/db/sqlite/driver.ts",
    "chars": 1634,
    "preview": "import { drizzle as DrizzleSqlite } from \"drizzle-orm/better-sqlite3\";\nimport Database from \"better-sqlite3\";\nimport * a"
  },
  {
    "path": "server/db/sqlite/index.ts",
    "chars": 184,
    "preview": "export * from \"./driver\";\nexport * from \"./logsDriver\";\nexport * from \"./safeRead\";\nexport * from \"./schema/schema\";\nexp"
  },
  {
    "path": "server/db/sqlite/logsDriver.ts",
    "chars": 267,
    "preview": "import { db as mainDb } from \"./driver\";\n\n// SQLite doesn't support separate databases for logs in the same way as Postg"
  },
  {
    "path": "server/db/sqlite/migrate.ts",
    "chars": 535,
    "preview": "import { migrate } from \"drizzle-orm/better-sqlite3/migrator\";\nimport { db } from \"./driver\";\nimport path from \"path\";\n\n"
  },
  {
    "path": "server/db/sqlite/safeRead.ts",
    "chars": 284,
    "preview": "import { db } from \"./driver\";\n\n/**\n * Runs a read query. For SQLite there is no replica/primary distinction,\n * so the "
  },
  {
    "path": "server/db/sqlite/schema/privateSchema.ts",
    "chars": 12847,
    "preview": "import { InferSelectModel } from \"drizzle-orm\";\nimport {\n    index,\n    integer,\n    real,\n    sqliteTable,\n    text\n} f"
  },
  {
    "path": "server/db/sqlite/schema/schema.ts",
    "chars": 44200,
    "preview": "import { randomUUID } from \"crypto\";\nimport { InferSelectModel } from \"drizzle-orm\";\nimport { index, integer, sqliteTabl"
  },
  {
    "path": "server/emails/index.ts",
    "chars": 1185,
    "preview": "export * from \"@server/emails/sendEmail\";\n\nimport nodemailer from \"nodemailer\";\nimport config from \"@server/lib/config\";"
  },
  {
    "path": "server/emails/sendEmail.ts",
    "chars": 1115,
    "preview": "import { render } from \"@react-email/render\";\nimport { ReactElement } from \"react\";\nimport emailClient from \"@server/ema"
  },
  {
    "path": "server/emails/templates/EnterpriseEditionKeyGenerated.tsx",
    "chars": 4587,
    "preview": "import React from \"react\";\nimport { Body, Head, Html, Preview, Tailwind } from \"@react-email/components\";\nimport { theme"
  },
  {
    "path": "server/emails/templates/NotifyResetPassword.tsx",
    "chars": 1658,
    "preview": "import React from \"react\";\nimport { Body, Head, Html, Preview, Tailwind } from \"@react-email/components\";\nimport { theme"
  },
  {
    "path": "server/emails/templates/NotifyUsageLimitApproaching.tsx",
    "chars": 2851,
    "preview": "import React from \"react\";\nimport { Body, Head, Html, Preview, Tailwind } from \"@react-email/components\";\nimport { theme"
  },
  {
    "path": "server/emails/templates/NotifyUsageLimitReached.tsx",
    "chars": 3310,
    "preview": "import React from \"react\";\nimport { Body, Head, Html, Preview, Tailwind } from \"@react-email/components\";\nimport { theme"
  },
  {
    "path": "server/emails/templates/ResetPasswordCode.tsx",
    "chars": 2209,
    "preview": "import React from \"react\";\nimport { Body, Head, Html, Preview, Tailwind } from \"@react-email/components\";\nimport { theme"
  },
  {
    "path": "server/emails/templates/ResourceOTPCode.tsx",
    "chars": 2235,
    "preview": "import React from \"react\";\nimport { Body, Head, Html, Preview, Tailwind } from \"@react-email/components\";\nimport {\n    E"
  },
  {
    "path": "server/emails/templates/SendInviteLink.tsx",
    "chars": 2895,
    "preview": "import React from \"react\";\nimport { Body, Head, Html, Preview, Tailwind } from \"@react-email/components\";\nimport { theme"
  },
  {
    "path": "server/emails/templates/SupportEmail.tsx",
    "chars": 1491,
    "preview": "import React from \"react\";\nimport { Body, Head, Html, Preview, Tailwind } from \"@react-email/components\";\nimport { theme"
  },
  {
    "path": "server/emails/templates/TwoFactorAuthNotification.tsx",
    "chars": 2682,
    "preview": "import React from \"react\";\nimport { Body, Head, Html, Preview, Tailwind } from \"@react-email/components\";\nimport { theme"
  },
  {
    "path": "server/emails/templates/VerifyEmailCode.tsx",
    "chars": 2024,
    "preview": "import React from \"react\";\nimport { Body, Head, Html, Preview, Tailwind } from \"@react-email/components\";\nimport { theme"
  },
  {
    "path": "server/emails/templates/WelcomeQuickStart.tsx",
    "chars": 5098,
    "preview": "import React from \"react\";\nimport { Body, Head, Html, Preview, Tailwind } from \"@react-email/components\";\nimport { theme"
  },
  {
    "path": "server/emails/templates/components/ButtonLink.tsx",
    "chars": 547,
    "preview": "import React from \"react\";\n\nexport default function ButtonLink({\n    href,\n    children,\n    className = \"\"\n}: {\n    hre"
  },
  {
    "path": "server/emails/templates/components/CopyCodeBox.tsx",
    "chars": 613,
    "preview": "import React from \"react\";\n\nconst DEFAULT_HINT = \"Copy and paste this code when prompted\";\n\nexport default function Copy"
  },
  {
    "path": "server/emails/templates/components/Email.tsx",
    "chars": 4314,
    "preview": "import React from \"react\";\nimport { Container, Img } from \"@react-email/components\";\nimport { build } from \"@server/buil"
  },
  {
    "path": "server/emails/templates/lib/theme.ts",
    "chars": 178,
    "preview": "import React from \"react\";\n\nexport const themeColors = {\n    theme: {\n        extend: {\n            colors: {\n          "
  },
  {
    "path": "server/extendZod.ts",
    "chars": 162,
    "preview": "import { extendZodWithOpenApi } from \"@asteasolutions/zod-to-openapi\";\nimport { z } from \"zod\";\n\nextendZodWithOpenApi(z)"
  }
]

// ... and 1053 more files (download for full content)

About this extraction

This page contains the full source code of the fosrl/pangolin GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 1253 files (8.9 MB), approximately 2.4M tokens, and a symbol index with 2674 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!