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
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ 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_CORS_ORIGINS=https://voidhash.localhost,https://mimic-admin.voidhash.localhost,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
Expand Down
22 changes: 13 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ on:
permissions:
contents: read

# Superseded PR pushes cancel their stale runs; main never cancels, so every
# main commit keeps a complete CI verdict.
concurrency:
group: repository-ci-${{ github.head_ref || github.ref }}
cancel-in-progress: true
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

env:
# Keeps turbo within the runner's 4 vCPUs; the scripts stay flag-free so the
Expand All @@ -35,17 +37,11 @@ jobs:
version: 11.1.3
run_install: false

- 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
with:
node-version: 22
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml

Expand All @@ -62,6 +58,14 @@ jobs:
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
run: pnpm install --frozen-lockfile

- name: Check publication boundary
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
run: node scripts/check-publication-boundary.mjs

- name: Check platform seam
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
run: node scripts/check-platform-seam.mjs

- name: Validate purchase and restore contracts
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
run: pnpm test:purchase-restore
Expand All @@ -73,6 +77,6 @@ jobs:
# 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)
- name: Verify (lint + typecheck + unit tier)
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
run: pnpm verify:quick
31 changes: 31 additions & 0 deletions .github/workflows/notify-mono.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Tells the monorepo that main moved, so its Bump voidhash workflow can open a
# submodule bump PR immediately (its daily cron is the fallback). MONO_DISPATCH_TOKEN
# must be a PAT that can send repository_dispatch to voidhashcom/voidhash-mono;
# when it is missing the step warns and succeeds, because the cron still covers
# detection.
name: Notify mono

on:
push:
branches: [main]

permissions:
contents: read

jobs:
dispatch:
name: Dispatch bump event
runs-on: blacksmith-2vcpu-ubuntu-2404
timeout-minutes: 5
steps:
- name: Send repository_dispatch to voidhash-mono
env:
GH_TOKEN: ${{ secrets.MONO_DISPATCH_TOKEN }}
run: |
if [ -z "$GH_TOKEN" ]; then
echo "::warning title=MONO_DISPATCH_TOKEN not set::Skipping the dispatch; voidhash-mono's daily bump cron remains the only drift detection."
exit 0
fi
gh api repos/voidhashcom/voidhash-mono/dispatches \
-f event_type=voidhash-main-push \
-f 'client_payload[sha]='"${GITHUB_SHA}"
2 changes: 2 additions & 0 deletions .github/workflows/osv-scanner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
- package.json
- pnpm-lock.yaml
- pnpm-workspace.yaml
- osv-scanner.toml
- apps/**/package.json
- examples/**/package.json
- libraries/**/package.json
Expand All @@ -18,6 +19,7 @@ on:
- package.json
- pnpm-lock.yaml
- pnpm-workspace.yaml
- osv-scanner.toml
- apps/**/package.json
- examples/**/package.json
- libraries/**/package.json
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
node-version: 24

- name: Setup pnpm
uses: pnpm/action-setup@v4
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/security-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
node-version: 24

- name: Setup pnpm
uses: pnpm/action-setup@v4
Expand All @@ -54,4 +54,7 @@ jobs:
run_install: false

- name: Reject Critical and High advisories
run: pnpm audit --prod --audit-level high
run: >-
pnpm audit --prod --audit-level high
--ignore GHSA-5p2g-fcmc-qvqq
--ignore GHSA-w3rx-r6r6-pgpr
4 changes: 2 additions & 2 deletions .github/workflows/selfhost.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ permissions:

concurrency:
group: selfhost-compose-${{ github.head_ref || github.ref }}
cancel-in-progress: true
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

env:
COMPOSE_PROJECT_NAME: voidhash-selfhost-ci-${{ github.run_id }}-${{ github.run_attempt }}
Expand All @@ -33,7 +33,7 @@ jobs:

- uses: actions/setup-node@v4
with:
node-version: 22
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/storekit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
node-version: 24

- name: Test StoreKit transaction retention
run: swift test --package-path libraries/react-native
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
22.23.2
24.19.0
25 changes: 24 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ for reporting guidance.

## Development

Voidhash uses Node.js 22 and pnpm 11. From the repository root:
Voidhash uses Node.js 24 and pnpm 11. From the repository root:

```sh
corepack enable
Expand All @@ -45,6 +45,29 @@ local Compose environment and its smoke tests.
Linting and formatting go through vite-plus: `pnpm lint` (`vp check`) and
`pnpm format` (`vp check --fix`).

`pnpm dev` starts every browser-facing development surface and the services
used by the Mimic example through Portless. The first run creates and trusts a
local certificate authority for the named HTTPS routes:

Run `pnpm dev` as your normal user, never through `sudo`. Portless elevates only
its HTTPS proxy when necessary, while the application processes remain owned by
your user. Startup also prunes orphaned Portless children left by crashed dev
sessions before checking the fixed ports. Use `pnpm dev:status` to inspect active
routes and `pnpm dev:doctor` to diagnose the proxy, certificate, or DNS setup.

| Surface | URL | App port |
| ------------------ | ---------------------------------------------- | -------- |
| Dashboard and docs | `https://voidhash.localhost` | `3000` |
| Mimic example API | `https://mimic-example-api.voidhash.localhost` | `3001` |
| Mimic admin | `https://mimic-admin.voidhash.localhost` | `3003` |
| Email previews | `https://emails.voidhash.localhost` | `3010` |
| Studio | `https://studio.voidhash.localhost` | `4830` |
| Mimic database | `https://mimic.voidhash.localhost` | `5001` |
| Mimic example | `https://mimic-example.voidhash.localhost` | `5173` |

The ports are strict: if another process is using one, startup fails instead of
silently moving an app and breaking its local links.

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
Expand Down
4 changes: 2 additions & 2 deletions apps/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:22-bookworm-slim AS build
FROM node:24-bookworm-slim AS build

ENV PNPM_HOME=/pnpm
ENV PATH=$PNPM_HOME:$PATH
Expand All @@ -16,7 +16,7 @@ RUN node scripts/check-selfhost-runtime-boundary.mjs /out
RUN rm -rf /www && corepack pnpm@11.1.3 --config.ignore-scripts=true --config.node-linker=hoisted --config.allow-unused-patches=true --filter @voidhash/www deploy --prod --legacy /www
RUN node scripts/check-selfhost-runtime-boundary.mjs /www

FROM node:22-bookworm-slim AS runtime
FROM node:24-bookworm-slim AS runtime

ENV NODE_ENV=production
ENV CHROMIUM_EXECUTABLE_PATH=/usr/bin/chromium-headless-shell
Expand Down
1 change: 1 addition & 0 deletions apps/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@voidhash/clickhouse-db": "workspace:*",
"@voidhash/core": "workspace:*",
"@voidhash/db": "workspace:*",
"@voidhash/lib": "workspace:*",
"@voidhash/mimic-core": "workspace:*",
"@voidhash/mimic-db": "workspace:*",
"@voidhash/mimic-schema": "workspace:*",
Expand Down
24 changes: 9 additions & 15 deletions apps/backend/src/DurableEntityAlarms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type {
DurableEntityAddress,
DurableEntityAlarmControlShape,
} from "@voidhash/platform/DurableEntity";
import { Effect } from "effect";
import { Clock, Effect } from "effect";

/** Handler for one durable-entity alarm type. */
export type DurableEntityAlarmHandler = (
Expand All @@ -19,18 +19,12 @@ export const dispatchDurableEntityAlarms = (
handlers: Readonly<Record<string, DurableEntityAlarmHandler>>,
now?: number,
): Effect.Effect<void, unknown> =>
Effect.suspend(() => {
const dispatchTime = now ?? Date.now();
return control
.listDueAlarms(dispatchTime, 100)
.pipe(
Effect.flatMap((due) =>
Effect.forEach(
due,
({ address }) =>
handlers[address.type]?.(address, dispatchTime) ?? Effect.void,
{ discard: true },
),
),
);
Effect.gen(function* () {
const dispatchTime = now ?? (yield* Clock.currentTimeMillis);
const due = yield* control.listDueAlarms(dispatchTime, 100);
yield* Effect.forEach(
due,
({ address }) => handlers[address.type]?.(address, dispatchTime) ?? Effect.void,
{ discard: true },
);
});
Loading
Loading