Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
333 changes: 0 additions & 333 deletions .cursor/rules/ultracite.mdc

This file was deleted.

5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@
**/.env
**/.env.*
*.log
**/ios/Pods
**/ios/build
**/android/build
**/android/.gradle
**/.expo
126 changes: 126 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# `production` for real deployments; `local-evaluation` relaxes credential
# validation for local development and is what the integration suites expect.
# Evaluation mode accepts the documented default root credentials; production
# refuses them. `pnpm stack:up` / `pnpm test:integration` force evaluation mode
# for the stack they manage, so this value is the one a real deployment gets.
SELFHOST_MODE=production
DATABASE_USERNAME=voidhash
DATABASE_PASSWORD=replace-with-a-random-password
DATABASE_NAME=voidhash
DATABASE_SSL=false
# Direct-TCP overrides for the migration process (`pnpm migrate`) and the local
# migration CLI (`pnpm db:migrate`). Each one falls back to its DATABASE_*
# counterpart, so leave them unset unless DATABASE_HOST points at a sandboxed or
# proxied endpoint — a connection broker, or a
# Hyperdrive-style local socket — that only resolves inside the runtime serving
# requests. Migrations run in their own process and need the origin address.
# DATABASE_DIRECT_HOST=postgres
# DATABASE_DIRECT_PORT=5432
# DATABASE_DIRECT_NAME=voidhash
# DATABASE_DIRECT_USERNAME=voidhash
# DATABASE_DIRECT_PASSWORD=replace-with-a-random-password
# DATABASE_DIRECT_SSL=false
# Overrides for platform state — cluster mailboxes, workflow executions,
# persisted queues, entity alarms, and the platform key-value store. Each falls
# back to its DATABASE_* counterpart, so leaving them unset keeps that state
# beside application data, which is what a deployment wants. They exist because a
# single-node cluster claims every shard in its database: a process that must not
# contend with the deployment for shards needs a database of its own, which is
# how `pnpm test:integration` isolates the suites that build their own cluster.
# DATABASE_PLATFORM_HOST=postgres
# DATABASE_PLATFORM_PORT=5432
# DATABASE_PLATFORM_NAME=voidhash
# DATABASE_PLATFORM_USERNAME=voidhash
# DATABASE_PLATFORM_PASSWORD=replace-with-a-random-password
# DATABASE_PLATFORM_SSL=false
# Optional analytics profile. Leave CLICKHOUSE_URL unset for the core stack.
# CLICKHOUSE_URL=http://clickhouse:8123
CLICKHOUSE_DATABASE=voidhash
CLICKHOUSE_ADMIN_USERNAME=voidhash_admin
CLICKHOUSE_ADMIN_PASSWORD=replace-with-a-random-password
CLICKHOUSE_USERNAME=voidhash_app
CLICKHOUSE_PASSWORD=replace-with-a-random-password
CLICKHOUSE_RO_USERNAME=voidhash_ro
CLICKHOUSE_RO_PASSWORD=replace-with-a-random-password
CLICKHOUSE_ANALYTICS_QUERY_USERNAME=voidhash_query
CLICKHOUSE_ANALYTICS_QUERY_PASSWORD=replace-with-a-random-password
CLICKHOUSE_HTTP_PORT=8123
MIMIC_ROOT_USERNAME=root
MIMIC_ROOT_PASSWORD=replace-with-a-random-password
PUBLIC_BASE_URL=http://localhost:5001
PUBLIC_FILES_BASE_URL=http://localhost:5001
MIMIC_CORS_ORIGINS=http://localhost:3000,http://localhost:3003
MIMIC_DOCUMENT_IDLE_NOTIFY_DEBOUNCE_MS=15000
MIMIC_PORT=5001
# The single root account. Voidhash self-host is single-player: these are the
# only credentials that can sign in, and there is no sign-up. Required in
# production mode; `local-evaluation` falls back to root / voidhash.
VOIDHASH_ROOT_USERNAME=root
VOIDHASH_ROOT_PASSWORD=replace-with-a-random-password
# Optional; defaults to root@voidhash.local. Used as the root user's address.
# VOIDHASH_ROOT_EMAIL=
# Signs the dashboard and API session tokens. Required in production mode.
VOIDHASH_AUTH_SECRET=replace-with-at-least-32-random-characters
# Durable agent model access. Configure at least one provider.
OPENAI_API_KEY=
ANTHROPIC_API_KEY=
# OPENAI_BASE_URL=https://your-openai-compatible-host/v1
# VOIDHASH_AGENT_MODEL_PROVIDER=openai
# VOIDHASH_AGENT_MODEL_ID=gpt-5.4
# VOIDHASH_AGENT_VISION_MODEL_PROVIDER=openai
# VOIDHASH_AGENT_VISION_MODEL_ID=gpt-5.4
# Required when Google Play RTDN is enabled. These must match the Pub/Sub push subscription.
GOOGLE_PUBSUB_PUSH_AUDIENCE=
GOOGLE_PUBSUB_PUSH_SERVICE_ACCOUNT_EMAIL=
# Optional offline Enterprise activation; configure the token and issuer verification key together.
VOIDHASH_LICENSE_KEY=
VOIDHASH_LICENSE_PUBLIC_KEY=
ENCRYPTION_KEY=
APNS_DELIVERY_ENABLED=false
EXCHANGE_RATE_API_KEY=
S3_ACCESS_KEY_ID=voidhash
S3_SECRET_ACCESS_KEY=replace-with-a-random-password
S3_REGION=us-east-1
S3_PUBLIC_BUCKET=voidhash-public
S3_ARTIFACT_BUCKET=voidhash-artifacts
MINIO_API_PORT=9000
MINIO_CONSOLE_PORT=9001
SMTP_HOST=mailpit
SMTP_PORT=1025
SMTP_SECURE=false
SMTP_REQUIRE_TLS=false
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_FROM_ADDRESS=noreply@voidhash.local
SMTP_FROM_NAME=Voidhash
SMTP_TLS_REJECT_UNAUTHORIZED=true
SMTP_VERIFY_ON_START=true
MAILPIT_SMTP_PORT=1025
MAILPIT_UI_PORT=8025

# ── Local development & integration tests ────────────────────────────────────
# Used together with docker-compose.dev.yml:
# docker compose -f docker-compose.yml -f docker-compose.dev.yml \
# --profile analytics up -d --build
# `pnpm test:integration` (repo root) reads this file and derives host-side
# connection settings from the values below, so the whole suite runs against
# this stack with no additional configuration.

# Host ports published by the dev overlay. Change them only when another local
# service already owns the default.
DATABASE_HOST_PORT=5432
COMPILER_HOST_PORT=5002

# To enable the analytics profile end-to-end (the compose service, migrations,
# and the ClickHouse integration suite), uncomment CLICKHOUSE_URL above.

# Browser used by the screenshot integration tests on the host. The container
# ships its own chromium; this is only for host-side test runs.
# PLATFORM_SELFHOST_CHROMIUM_EXECUTABLE_PATH=/Applications/Google Chrome.app/Contents/MacOS/Google Chrome

# ── Values you must provide ──────────────────────────────────────────────────
# VOIDHASH_ROOT_PASSWORD / VOIDHASH_AUTH_SECRET — sign-in. Production mode
# refuses to start until both hold real values.
# OPENAI_API_KEY / ANTHROPIC_API_KEY — required only for the AI designer agent.
# EXCHANGE_RATE_API_KEY — required only for the FX rate sync job.
# ENCRYPTION_KEY — required for payment-provider credential storage.
19 changes: 13 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ concurrency:
group: repository-ci-${{ github.head_ref || github.ref }}
cancel-in-progress: true

env:
# Keeps turbo within the runner's 4 vCPUs; the scripts stay flag-free so the
# local and CI invocations are the same command.
TURBO_CONCURRENCY: 2

jobs:
validate:
name: Validate
Expand All @@ -33,6 +38,9 @@ jobs:
- name: Check publication boundary
run: node scripts/check-publication-boundary.mjs

- name: Check platform seam
run: node scripts/check-platform-seam.mjs

- name: Setup Node.js
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
uses: actions/setup-node@v4
Expand Down Expand Up @@ -62,10 +70,9 @@ jobs:
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
run: pnpm build --concurrency=2

- name: Typecheck
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
run: pnpm typecheck --concurrency=2

- name: Test
# The same command developers run locally. The stack-backed tiers
# (`test:integration`, `test:e2e`) run in the Self-host Compose workflow,
# which owns the Compose lifecycle; together they cover `pnpm verify`.
- name: Verify (typecheck + unit tier)
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
run: pnpm test --concurrency=2
run: pnpm verify:quick
64 changes: 47 additions & 17 deletions .github/workflows/selfhost.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,39 +40,69 @@ jobs:
- name: Install workspace dependencies
run: pnpm install --frozen-lockfile

- name: Install Chromium
run: pnpm exec playwright-core install --with-deps chromium

# The stack and the host-side tiers authenticate against each other, so
# they have to read one environment: the repo-root `.env` a developer
# keeps (and `pnpm test:integration` creates on a first checkout). It must
# exist *before* the stack starts — Compose otherwise falls back to the
# built-in defaults in the compose file while the tiers read the file, and
# every host-side connection fails authentication. Compose consumes it via
# `--env-file`; the tiers that read `process.env` rather than the file (the
# two smoke tiers) get the same values from the job environment.
- name: Prepare the stack environment
run: |
cp .env.example .env
# `.env.example` is a deployment template, so it selects production
# mode, which refuses its own placeholder secrets. This is a loopback
# CI stack: `pnpm stack:up` forces the same mode locally.
sed -i 's|^SELFHOST_MODE=.*|SELFHOST_MODE=local-evaluation|' .env
# The thumbnail assertions wait on the idle debounce.
sed -i 's|^MIMIC_DOCUMENT_IDLE_NOTIFY_DEBOUNCE_MS=.*|MIMIC_DOCUMENT_IDLE_NOTIFY_DEBOUNCE_MS=250|' .env
grep -E '^[A-Z][A-Z0-9_]*=' .env >> "$GITHUB_ENV"

- name: Start stateful stores
run: docker compose -f selfhost/docker-compose.yml --profile analytics up -d clickhouse minio --wait --wait-timeout 180
run: docker compose -f selfhost/docker-compose.yml -f selfhost/docker-compose.dev.yml --env-file .env --profile analytics up -d clickhouse minio --wait --wait-timeout 180

- name: Initialize object store
run: docker compose -f selfhost/docker-compose.yml --profile analytics run --rm minio-init

- name: Verify S3-compatible object store
env:
PLATFORM_NODE_S3_ENDPOINT: http://127.0.0.1:9000
PLATFORM_NODE_S3_TEST: 1
run: pnpm --filter @voidhash/platform-node exec vp test run -c vitest.mts tests/S3ObjectStore.integration.test.ts
run: docker compose -f selfhost/docker-compose.yml -f selfhost/docker-compose.dev.yml --env-file .env --profile analytics run --rm minio-init

# The dev overlay publishes Postgres and the compiler, which the
# host-side integration tier connects to. `CLICKHOUSE_URL` stays a shell
# override rather than an entry in `.env`: it names the compose-internal
# endpoint the application dials, while every host-side tier reaches
# ClickHouse on the published port instead.
- name: Build and start Community Compose
env:
CLICKHOUSE_URL: http://clickhouse:8123
MIMIC_DOCUMENT_IDLE_NOTIFY_DEBOUNCE_MS: 250
run: docker compose -f selfhost/docker-compose.yml --profile analytics up --build --wait --wait-timeout 180
run: docker compose -f selfhost/docker-compose.yml -f selfhost/docker-compose.dev.yml --env-file .env --profile analytics up --build --wait --wait-timeout 180

- name: Reclaim image build cache
run: docker builder prune --all --force

- name: Run self-host smoke
run: pnpm exec tsx selfhost/smoke.mts
# Same scripts developers run locally; this workflow owns the stack the
# two tiers need. With the unit tier in Repository CI, this completes the
# coverage of `pnpm verify`.
- name: Integration tier
run: pnpm test:integration

- name: End-to-end tier
run: pnpm test:e2e

- name: Run self-host release smoke
run: pnpm exec tsx selfhost/release-smoke.mts
- name: End-to-end tier (release)
run: pnpm test:e2e:release

# These two run even when the environment step never did, so they stay off
# `--env-file` (a missing file is a hard Compose error). They need no
# interpolated value: both address the project by name, and the job
# environment already carries what `.env` set.
- name: Show Compose diagnostics
if: always()
run: |
docker compose -f selfhost/docker-compose.yml --profile analytics ps || true
docker compose -f selfhost/docker-compose.yml --profile analytics logs --no-color || true
docker compose -f selfhost/docker-compose.yml -f selfhost/docker-compose.dev.yml --profile analytics ps || true
docker compose -f selfhost/docker-compose.yml -f selfhost/docker-compose.dev.yml --profile analytics logs --no-color || true

- name: Stop Compose
if: always()
run: docker compose -f selfhost/docker-compose.yml --profile analytics down --volumes --remove-orphans
run: docker compose -f selfhost/docker-compose.yml -f selfhost/docker-compose.dev.yml --profile analytics down --volumes --remove-orphans
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
22.10.0
22.23.2
11 changes: 6 additions & 5 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[javascript][typescript][javascriptreact][typescriptreact][json][jsonc][css][graphql]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[javascript]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
"[javascriptreact]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
"[typescript]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
"[typescriptreact]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
"npm.scriptRunner": "vp",
"typescript.tsdk": "node_modules/typescript/lib",
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"emmet.showExpandedAbbreviation": "never",
"editor.codeActionsOnSave": {
"source.fixAll.biome": "explicit",
"source.organizeImports.biome": "explicit"
"source.fixAll.oxc": "explicit"
},
"files.exclude": {}
}
41 changes: 29 additions & 12 deletions .zed/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,66 @@
"JavaScript": {
"formatter": {
"language_server": {
"name": "biome"
"name": "oxfmt"
}
},
"code_actions_on_format": {
"source.fixAll.biome": true,
"source.organizeImports.biome": true
"source.fixAll.oxc": true,
"source.organizeImports.oxc": true
}
},
"TypeScript": {
"formatter": {
"language_server": {
"name": "biome"
"name": "oxfmt"
}
},
"code_actions_on_format": {
"source.fixAll.biome": true,
"source.organizeImports.biome": true
"source.fixAll.oxc": true,
"source.organizeImports.oxc": true
}
},
"JSX": {
"formatter": {
"language_server": {
"name": "biome"
"name": "oxfmt"
}
},
"code_actions_on_format": {
"source.fixAll.biome": true,
"source.organizeImports.biome": true
"source.fixAll.oxc": true,
"source.organizeImports.oxc": true
}
},
"TSX": {
"formatter": {
"language_server": {
"name": "biome"
"name": "oxfmt"
}
},
"code_actions_on_format": {
"source.fixAll.biome": true,
"source.organizeImports.biome": true
"source.fixAll.oxc": true,
"source.organizeImports.oxc": true
}
}
},
"lsp": {
"oxlint": {
"initialization_options": {
"settings": {
"run": "onType",
"fixKind": "safe_fix",
"typeAware": true,
"unusedDisableDirectives": "deny"
}
}
},
"oxfmt": {
"initialization_options": {
"settings": {
"run": "onSave"
}
}
},
"typescript-language-server": {
"settings": {
"typescript": {
Expand Down
11 changes: 11 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ Use `pnpm check:publication` to validate license metadata and the public/private
repository boundary. The [self-hosting guide](selfhost/README.md) documents the
local Compose environment and its smoke tests.

Linting and formatting go through vite-plus: `pnpm lint` (`vp check`) and
`pnpm format` (`vp check --fix`).

The steps above describe a **standalone clone** of this repository, which installs
its own `node_modules` from this repository's lockfile. This repository is also
consumed as a nested workspace by Voidhash's private monorepo. In that mode the
superproject's root install is authoritative: it already covers every package here,
this directory must **not** have its own `node_modules` (two installs give
`drizzle-orm`/`@types/react` duplicate TypeScript type identities), and all commands
are run from the superproject root rather than from here.

## Testing

Run the smallest relevant package tests while iterating, then run the repository
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ run the Community platform locally, see the [self-hosting guide](selfhost/README
The [architecture overview](docs/architecture.md) explains the Community,
Cloud, and Enterprise composition boundaries, and the
[licensing and self-hosting FAQ](docs/licensing-and-self-hosting-faq.md) covers
AGPL and the current BYO WorkOS requirement.
AGPL and the self-hosting model.

## 🤝 Contributing

Expand Down
Loading
Loading