Building a modern alternative to Salesforce, powered by the community.
Find a file
Félix Malfait 75848ff8ea
Some checks are pending
CD deploy main / deploy-main (push) Waiting to run
CI Create App E2E minimal / changed-files-check (push) Waiting to run
CI Create App E2E minimal / create-app-e2e-minimal (push) Blocked by required conditions
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Blocked by required conditions
CI Emails / emails-test (push) Blocked by required conditions
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Blocked by required conditions
CI Example App Postcard / changed-files-check (push) Waiting to run
CI Example App Postcard / example-app-postcard (push) Blocked by required conditions
CI Example App Postcard / ci-example-app-postcard-status-check (push) Blocked by required conditions
Push translations to Crowdin / Extract and upload translations (push) Waiting to run
CI Create App / changed-files-check (push) Waiting to run
CI Create App / create-app-test (lint) (push) Blocked by required conditions
CI Create App / create-app-test (test) (push) Blocked by required conditions
CI Create App / create-app-test (typecheck) (push) Blocked by required conditions
CI Create App / ci-create-app-status-check (push) Blocked by required conditions
CI Docs / changed-files-check (push) Waiting to run
CI Docs / docs-lint (push) Blocked by required conditions
CI Emails / changed-files-check (push) Waiting to run
CI Emails / ci-emails-status-check (push) Blocked by required conditions
CI Example App Hello World / changed-files-check (push) Waiting to run
CI Example App Hello World / example-app-hello-world (push) Blocked by required conditions
feat: move admin panel to dedicated /admin-panel GraphQL endpoint (#19852)
## Summary

Splits admin-panel resolvers off the shared `/metadata` GraphQL endpoint
onto a dedicated `/admin-panel` endpoint. The backend plumbing mirrors
the existing `metadata` / `core` pattern (new scope, decorator, module,
factory), and admin types now live in their own
`generated-admin/graphql.ts` on the frontend — dropping 877 lines of
admin noise from `generated-metadata`.

## Why

- **Smaller attack surface on `/metadata`** — every authenticated user
hits that endpoint; admin ops don't belong there.
- **Independent complexity limits and monitoring** per endpoint.
- **Cleaner module boundaries** — admin is a cross-cutting concern that
doesn't match the "shared-schema configuration" meaning of `/metadata`.
- **Deploy / blast-radius isolation** — a broken admin query can't
affect `/metadata`.

Runtime behavior, auth, and authorization are unchanged — this is a
relocation, not a re-permissioning. All existing guards
(`WorkspaceAuthGuard`, `UserAuthGuard`,
`SettingsPermissionGuard(SECURITY)` at class level; `AdminPanelGuard` /
`ServerLevelImpersonateGuard` at method level) remain on
`AdminPanelResolver`.

## What changed

### Backend
- `@AdminResolver()` decorator with scope `'admin'`, naming parallels
`CoreResolver` / `MetadataResolver`.
- `AdminPanelGraphQLApiModule` + `adminPanelModuleFactory` registered at
`/admin-panel`, same Yoga hook set as the metadata factory (Sentry
tracing, error handler, introspection-disabling in prod, complexity
validation).
- Middleware chain on `/admin-panel` is identical to `/metadata`.
- `@nestjs/graphql` patch extended: `resolverSchemaScope?: 'core' |
'metadata' | 'admin'`.
- `AdminPanelResolver` class decorator swapped from
`@MetadataResolver()` to `@AdminResolver()` — no other changes.

### Frontend
- `codegen-admin.cjs` → `src/generated-admin/graphql.ts` (982 lines).
- `codegen-metadata.cjs` excludes admin paths; metadata file shrinks by
877 lines.
- `ApolloAdminProvider` / `useApolloAdminClient` follow the existing
`ApolloCoreProvider` / `useApolloCoreClient` pattern, wired inside
`AppRouterProviders` alongside the core provider.
- 37 admin consumer files migrated: imports switched to
`~/generated-admin/graphql` and `client: useApolloAdminClient()` is
passed to `useQuery` / `useMutation`.
- Three files intentionally kept on `generated-metadata` because they
consume non-admin Documents: `useHandleImpersonate.ts`,
`SettingsAdminApplicationRegistrationDangerZone.tsx`,
`SettingsAdminApplicationRegistrationGeneralToggles.tsx`.

### CI
- `ci-server.yaml` runs all three `graphql:generate` configurations and
diff-checks all three generated dirs.

## Authorization (unchanged, but audited while reviewing)

Every one of the 38 methods on `AdminPanelResolver` has a method-level
guard:
- `AdminPanelGuard` (32 methods) — requires `canAccessFullAdminPanel ===
true`
- `ServerLevelImpersonateGuard` (6 methods: user/workspace lookup + chat
thread views) — requires `canImpersonate === true`

On top of the class-level guards above. No resolver method is accessible
without these flags + `SECURITY` permission in the workspace.

## Test plan

- [ ] Dev server boots; `/graphql`, `/metadata`, `/admin-panel` all
mapped as separate GraphQL routes (confirmed locally during
development).
- [ ] `nx typecheck twenty-server` passes.
- [ ] `nx typecheck twenty-front` passes.
- [ ] `nx lint:diff-with-main twenty-server` and `twenty-front` both
clean.
- [ ] Manual smoke test: log in with a user who has
`canAccessFullAdminPanel=true`, open the admin panel at
`/settings/admin-panel`, verify each tab loads (General, Health, Config
variables, AI, Apps, Workspace details, User details, chat threads).
- [ ] Manual smoke test: log in with a user who has
`canImpersonate=false` and `canAccessFullAdminPanel=false`, hit
`/admin-panel` directly with a raw GraphQL request, confirm permission
error on every operation.
- [ ] Production deploy note: reverse proxy / ingress must route the new
`/admin-panel` path to the Nest server. If the proxy has an explicit
allowlist, infra change required before cutover.

## Follow-ups (out of scope here)

- Consider cutting over the three
`SettingsAdminApplicationRegistration*` components to admin-scope
versions of the app-registration operations so the admin page is fully
on the admin endpoint.
- The `renderGraphiQL` double-assignment in
`admin-panel.module-factory.ts` is copied from
`metadata.module-factory.ts` — worth cleaning up in both.
2026-04-19 20:55:10 +02:00
.claude-pr Upgrade command internal doc (#19541) 2026-04-10 09:43:06 +00:00
.cursor Rename standard and custom apps (#19631) 2026-04-13 13:13:59 +00:00
.github feat: move admin panel to dedicated /admin-panel GraphQL endpoint (#19852) 2026-04-19 20:55:10 +02:00
.vscode Migrate from ESLint to OxLint (#18443) 2026-03-06 01:03:50 +01:00
.yarn Refactor dependency graph for SDK, client-sdk and create-app (#18963) 2026-03-26 10:56:52 +00:00
packages feat: move admin panel to dedicated /admin-panel GraphQL endpoint (#19852) 2026-04-19 20:55:10 +02:00
.dockerignore Scaffold light twenty app dev container (#18734) 2026-03-18 20:10:54 +01:00
.gitattributes Consolidate Prettier config and improve consistency (#15191) 2025-10-18 12:24:35 +02:00
.gitignore perf(sdk): split twenty-sdk barrel into per-purpose subpaths to cut logic-function bundle ~700x (#19834) 2026-04-18 19:38:34 +02:00
.mcp.json Fix AI chat re-renders and refactored code (#18585) 2026-03-21 12:52:21 +00:00
.nvmrc Upgrade to Node 24 (#13730) 2025-08-07 17:02:12 +02:00
.yarnrc.yml Refactor dependency graph for SDK, client-sdk and create-app (#18963) 2026-03-26 10:56:52 +00:00
CLAUDE.md Upgrade command internal doc (#19541) 2026-04-10 09:43:06 +00:00
jest.preset.js Move tools/eslint-rules to packages/twenty-eslint-rules (#17203) 2026-01-17 07:37:17 +01:00
LICENSE feat(sso): allow to use OIDC and SAML (#7246) 2024-10-21 20:07:08 +02:00
nx.json fix: replace npm pkg set with node script in set-local-version target (#19344) 2026-04-05 18:56:37 +00:00
package.json Bump @storybook/react-vite from 10.2.13 to 10.3.3 (#19232) 2026-04-02 08:49:11 +00:00
README.md docs: fix contributor docs links and typos (#18637) 2026-03-14 12:54:31 +01:00
tsconfig.base.json Revert "[hacktoberfest] feat: add fireflies" (#15589) 2025-11-04 12:25:23 +01:00
yarn.config.cjs [ENHC] Create Yarn constraints to validate node version (#10542) 2025-02-27 15:18:07 +01:00
yarn.lock feat: move admin panel to dedicated /admin-panel GraphQL endpoint (#19852) 2026-04-19 20:55:10 +02:00

Twenty logo

The #1 Open-Source CRM

🌐 Website · 📚 Documentation · Roadmap · Discord · Figma


Cover


Installation

See: 🚀 Self-hosting 🖥️ Local Setup

Why Twenty

We built Twenty for three reasons:

CRMs are too expensive, and users are trapped. Companies use locked-in customer data to hike prices. It shouldn't be that way.

A fresh start is required to build a better experience. We can learn from past mistakes and craft a cohesive experience inspired by new UX patterns from tools like Notion, Airtable or Linear.

We believe in open-source and community. Hundreds of developers are already building Twenty together. Once we have plugin capabilities, a whole ecosystem will grow around it.


What You Can Do With Twenty

Please feel free to flag any specific needs you have by creating an issue.

Below are a few features we have implemented to date:

Personalize layouts with filters, sort, group by, kanban and table views

Companies Kanban Views

Customize your objects and fields

Setting Custom Objects

Create and manage permissions with custom roles

Permissions

Automate workflow with triggers and actions

Workflows

Emails, calendar events, files, and more

Other Features


Stack

Thanks

Chromatic Greptile Sentry Crowdin E2B

Thanks to these amazing services that we use and recommend for UI testing (Chromatic), code review (Greptile), catching bugs (Sentry) and translating (Crowdin).

Join the Community