## Summary Slims `preview-env-dispatch.yaml` to a single dispatch and deletes `preview-env-keepalive.yaml`. The actual preview-env work moves to **twentyhq/ci-privileged#22** (must merge as a pair). ## Why Context: PR #20867 was a credential-exfil attempt against our workflows. GitHub's default fork-PR-no-secrets policy + our existing gates (`author_association` checks, `pull_request_target` checking out base, `enableScripts: false`) neutralized the actual attack — but the audit surfaced one workflow that *would* have given a malicious external PR access to a real secret if a maintainer had applied the `preview-app` label: `preview-env-keepalive.yaml`. That workflow checked out the PR head SHA, did `docker login` with `DOCKERHUB_PASSWORD`, then ran the PR's `docker-compose.yml`. A malicious compose could have mounted `~/.docker/config.json` and exfiltrated the Dockerhub credential. After this PR, that workflow lives in `twentyhq/ci-privileged` instead, paired with a rename of the credential to `DOCKERHUB_RO_TOKEN` (Dockerhub PAT with `Public Repo Read-only` scope). A read-only PAT has no exfiltration value — it's equivalent to anonymous Dockerhub access plus rate-limit headroom — so the credential lives safely on the runner without further hygiene tricks. ## What this PR does - **Modifies** `.github/workflows/preview-env-dispatch.yaml`: - Single dispatch to `twentyhq/ci-privileged` (was: self-dispatch to twenty for the env + a separate dispatch to ci-privileged for the PR comment). - `permissions: {}` (was: `contents: write`). - Drops `preview-env-keepalive.yaml` from the path-trigger list. - **Deletes** `.github/workflows/preview-env-keepalive.yaml`. The 207-line workflow now lives in `twentyhq/ci-privileged/.github/workflows/preview-env.yaml`. Net `twenty` repo change: **-204 lines / +3 lines**. ## Companion PR twentyhq/ci-privileged#22 — adds the new `preview-env.yaml`, deletes the now-redundant `post-preview-comment.yaml`. ## Secrets fallout in this repo After this PR, `DOCKERHUB_PASSWORD` in `twentyhq/twenty` secrets is only used by `ci-test-docker-compose.yaml`, where: - It evaluates to empty for fork PRs (GitHub default — secrets aren't passed to fork-PR workflows). - It's only needed for internal / merge_queue runs, for Dockerhub rate-limit headroom on base-image pulls. Recommend (separate change): also convert the twenty-side `DOCKERHUB_PASSWORD` to a `Public Repo Read-only` Dockerhub PAT, and rename it to `DOCKERHUB_RO_TOKEN` for consistency with ci-privileged. The workflow change for `ci-test-docker-compose.yaml` would just be a rename — login flow is identical for password vs. PAT. ## Test plan - [ ] Merge twentyhq/ci-privileged#22 first (so the dispatched event has a handler) - [ ] Open an internal PR touching `packages/twenty-docker/**`, confirm `Preview Environment Dispatch` runs and ci-privileged's `Preview Environment` workflow runs the docker compose + posts the URL - [ ] On an external contributor PR, apply the `preview-app` label, confirm the same flow - [ ] Confirm closing the PR doesn't break (no cleanup workflow was changed)
The #1 Open-Source CRM
Website ·
Documentation ·
Roadmap ·
Discord ·
Figma
Why Twenty
Twenty gives technical teams the building blocks for a custom CRM that meets complex business needs and quickly adapts as the business evolves. Twenty is the CRM you build, ship, and version like the rest of your stack.
Learn more about why we built Twenty
Installation
Cloud
The fastest way to get started. Sign up at twenty.com and spin up a workspace in under a minute, with no infrastructure to manage and always up to date.
Build an app
Scaffold a new app with the Twenty CLI:
npx create-twenty-app my-app
Define objects, fields, and views as code:
import { defineObject, FieldType } from 'twenty-sdk/define';
export default defineObject({
nameSingular: 'deal',
namePlural: 'deals',
labelSingular: 'Deal',
labelPlural: 'Deals',
fields: [
{ name: 'name', label: 'Name', type: FieldType.TEXT },
{ name: 'amount', label: 'Amount', type: FieldType.CURRENCY },
{ name: 'closeDate', label: 'Close Date', type: FieldType.DATE_TIME },
],
});
Then ship it to your workspace:
npx twenty app:publish --private
See the app development guide for objects, views, agents, and logic functions.
Self-hosting
Run Twenty on your own infrastructure with Docker Compose, or contribute locally via the local setup guide.
Everything you need
Twenty gives you the building blocks of a modern CRM (objects, views, workflows, and agents) and lets you extend them as code. Here's a tour of what's in the box.
Want to go deeper? Read the User Guide for product walkthroughs, or the
Documentation for developer reference.
|
|
|
|
|
|
Stack
TypeScript
Nx
NestJS, with BullMQ,
PostgreSQL,
Redis
React, with Jotai, Linaria and Lingui
Thanks
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
Star the repo ·
Discord ·
Feature requests ·
Releases ·
X ·
LinkedIn ·
Crowdin ·
Contribute





