Get started
OpenAct runs on your own Cloudflare account. You need a Cloudflare account and a Resend account for sign-in emails.
Deploy with one click
- Click Deploy to Cloudflare.
- Sign in to Cloudflare. Connect GitHub or GitLab. Cloudflare copies this repository to your account.
- Cloudflare creates the D1 database, the R2 bucket and the Vectorize index for you.
- Fill in the form:
RESEND_API_KEY: your Resend API key.AUTH_SECRET: a long random string. Make one withopenssl rand -hex 32.EMAIL_FROM: the sender, for exampleOpenAct <no-reply@yourdomain.com>.
- Click Deploy. The deploy script applies the database migrations, then deploys the Worker.
After the first deploy:
- In Resend, verify the domain that you use in
EMAIL_FROM. Without this step, sign-in emails do not arrive. - In the Cloudflare dashboard, go to AI > AI Gateway and make a gateway named
openact. If the gateway does not exist, AI requests still work, but without gateway logs and limits. - Open your Worker URL. Type your email. The first person can always sign up.
- Search by meaning needs a Vectorize index with 768 dimensions and a
project_idmetadata index. If search shows "Search by words" only, runnpm run search:setuponce from a copy of the repository. Search by words works without it.
Deploy by hand
You need Node.js 20 or later and a Cloudflare account.
git clone https://github.com/marketgrowth/openact.git
cd openact
npm install
npx wrangler login
# Make the database. Copy the database_id from the output into wrangler.jsonc, under d1_databases.
npx wrangler d1 create openact
# Make the file bucket.
npx wrangler r2 bucket create openact-files
# Make the search index (768 dimensions, cosine, with a project_id metadata index).
npm run search:setup
# Add the secrets.
npx wrangler secret put RESEND_API_KEY
npx wrangler secret put AUTH_SECRET
# Set EMAIL_FROM and the other settings in wrangler.jsonc under "vars". Then build and deploy.
npm run build
npm run deploynpm run deploy applies new migrations, checks the search index, then deploys. Use it for every update.
Run it on your computer
npm install
cp .dev.vars.example .dev.vars # optional
npm run devOpen http://localhost:8787.
- Without
RESEND_API_KEY, OpenAct does not send email. It prints each sign-in code in the terminal. npm run devruns fully local, so AI assist and search by meaning are off. Search by words still works. To use AI on your computer, runnpx wrangler login, thennpm run dev:ai.- For hot reload of the web app, run
npx wrangler dev --localin one terminal andnpm run dev:uiin a second terminal. Then open http://localhost:5173.
Other commands:
| Command | What it does |
|---|---|
npm run typecheck | Checks the types of the Worker and the web app |
npm test | Runs the unit tests |
npm run test:search | Runs the search pipeline test: real migrations in SQLite, with fake Vectorize and Workers AI |
npm run search:setup | Creates the Vectorize index and its metadata index. Safe to run again. |
npm run build | Builds the web app into dist/client |
npm run db:migrate:local | Applies migrations to the local database |
npm run db:migrate:remote | Applies migrations to the Cloudflare database |
Settings
Set these under vars in wrangler.jsonc, or in the Cloudflare dashboard.
| Name | Default | What it does |
|---|---|---|
APP_NAME | OpenAct | The default name in the app and in emails. A super admin can change it in Site settings. |
SUPER_ADMIN_EMAILS | empty | A comma-separated list of the emails that can change the app name, the logo and which apps are on for the site. Empty means nobody can. |
APP_URL | empty | The public URL used in email links. Empty means the URL of the request. |
EMAIL_FROM | OpenAct <no-reply@example.com> | The sender address. The domain must be verified in Resend. |
SIGNUP_MODE | open | open: anyone can sign up. invite: only invited people can sign up. |
ALLOWED_EMAIL_DOMAINS | empty | A comma-separated list of domains that can sign up, for example example.com,example.org |
AI_PROVIDER | workers-ai | workers-ai, anthropic or openai. This is the deploy default. A company owner can choose Claude with their own key. Read The AI provider of a company. |
AI_MODEL | @cf/meta/llama-3.3-70b-instruct-fp8-fast | The model ID for the provider |
AI_EMBED_MODEL | @cf/baai/bge-base-en-v1.5 | The Workers AI model for search embeddings. It must make 768-dimension vectors. |
AI_SCOPE_MODEL | (empty) | A small, fast model for the topic check. Empty uses @cf/meta/llama-3.1-8b-instruct-fast, claude-haiku-5-5 or gpt-4.1-mini, by provider. |
AI_GATEWAY_ID | openact | The AI Gateway name. Empty turns the gateway off. |
CF_ACCOUNT_ID | empty | Your account ID. Needed for anthropic and openai. |
TYPESAFE_MODEL | jev-1.14.0 | The Jev release for the topic check. Pinned, because the thresholds are set for it. |
TYPESAFE_BASE_URL | https://api.typesafe.ai | Another URL for the TypeSafe API, such as a gateway |
Set these secrets with npx wrangler secret put NAME:
| Secret | Needed | What it does |
|---|---|---|
RESEND_API_KEY | Yes | Sends sign-in codes and invitations |
AUTH_SECRET | Yes | Protects stored sign-in codes, the encrypted AI keys of companies and the saved Docs share links. Use 32 or more random characters. If you change it, company owners must paste their AI keys again. Old share links still open, but editors must make a new link to copy one again. |
ANTHROPIC_API_KEY | For anthropic | Anthropic API key, sent through AI Gateway |
OPENAI_API_KEY | For openai | OpenAI API key, sent through AI Gateway |
AI_GATEWAY_TOKEN | No | Needed only if your AI Gateway uses authentication |
TYPESAFE_API_KEY | No | Turns on Jev for the topic check. Get a key from TypeSafe. |
The first person to sign in can always make an account, even in invite mode. An invitation always lets that email sign up, even when ALLOWED_EMAIL_DOMAINS does not include its domain.
Durable Objects
wrangler.jsonc binds two Durable Object classes. Wrangler makes them on the first deploy.
| Binding | Class | What it does |
|---|---|---|
PROJECT_ROOMS | ProjectRoom | Live updates for projects and docs lists. Without it, pages check for changes every few seconds. |
DOCS_ROOMS | DocsRoom | Live editing in Docs. Without it, Docs saves the usual way. |
Site name, logo and apps
A super admin sets the name and logo that everyone on the site sees, and can turn an app off for the whole site.
Add your email to
SUPER_ADMIN_EMAILSinwrangler.jsonc, then deploy.Sign in. Open your menu at the bottom of the app rail, then choose Site settings.
Type the app name, then choose Save name. Leave it empty to use
APP_NAMEagain.Choose Upload logo. Use a PNG, JPEG or WebP image, up to 1 MB. A square image looks best.
Under Apps, turn an app off to hide it in every company. Its data stays. Companies cannot turn it on again until a super admin does.
The name shows on the sign-in page, in the browser tab, in the company menu and in emails. The logo shows on the sign-in page and as the browser tab icon. Without a logo, the OpenAct mark shows.
A super admin is not a company role. Super admins change only the site settings. They get no access to company data.
An app that is off for the site is off in every company: its pages, its API and its public links stop working. You cannot turn every app off. Keep at least one on.
Each Worker keeps the settings for up to 30 seconds. After a change, other open pages show it when they load again.
Security headers
The file public/_headers sets the security headers for the web app. Cloudflare sends them with index.html and every static file.
Content-Security-Policylets the page load scripts, styles, images and data only from your own site. Fonts can also come from Google Fonts. Other sites cannot show the app in a frame.Strict-Transport-Securitytells browsers to use only HTTPS for one year.- The public build has no source maps. The Worker's source maps go only to Cloudflare, for error stack traces.
If an app loads files from another site, add that site to the matching directive in public/_headers. For example, add https://images.example.com to img-src.
Delete an account
A person can delete their own account. They open Account from their menu, choose Delete account, then type their email to confirm.
- They leave every company, and their sessions end.
- Projects, tasks and files that they made stay with the company. Their name leaves them: assignments and "created by" fields become empty.
- The only owner of a company cannot delete their account. They must make another person an owner, or delete the company, first.
How sign-in works
- A person types their email. OpenAct makes a random 6-digit code and sends it with Resend.
- The code works for 10 minutes and for 5 tries. Only the newest code works.
- When the code is correct, OpenAct sets a session cookie for 30 days. The cookie is
HttpOnlyandSameSite=Lax, andSecureon HTTPS. - Pending project and company invitations for that email become memberships. If a company claimed the email's domain, the person joins it as a member.
OpenAct stores only SHA-256 hashes of codes and session tokens. It limits code requests to 5 for each email in 15 minutes and 30 for each IP address in one hour. A daily cron job deletes old codes and sessions.