Setup, deployment, and maintenance scripts for managing a SiliconBeest instance.
All scripts share a central configuration via config.sh -- no resource names are hardcoded.
SiliconBeest uses a unified worker architecture:
siliconbeest-- single Cloudflare Worker that serves both the Vue frontend and the API/ActivityPub backend. Deployed fromsiliconbeest/.siliconbeest-queue-consumer-- separate worker that processes federation and internal queues.siliconbeest-email-sender-- separate worker that processes the email queue.
Every script sources config.sh which defines all resource names based on a single PROJECT_PREFIX (default: siliconbeest).
| Variable | Default | Description |
|---|---|---|
PROJECT_PREFIX |
siliconbeest |
Master prefix -- changes all defaults |
MAIN_WORKER_NAME |
{prefix} |
Unified worker name (Vue + API) |
CONSUMER_NAME |
{prefix}-queue-consumer |
Queue Consumer name |
EMAIL_SENDER_NAME |
{prefix}-email-sender |
Email Sender Worker name |
D1_DATABASE_NAME |
{prefix}-db |
D1 database name |
R2_BUCKET_NAME |
{prefix}-media |
R2 bucket name |
KV_CACHE_TITLE |
{prefix}-CACHE |
KV namespace for cache |
KV_SESSIONS_TITLE |
{prefix}-SESSIONS |
KV namespace for sessions |
KV_FEDIFY_TITLE |
{prefix}-FEDIFY_KV |
KV namespace for Fedify federation state |
QUEUE_FEDERATION |
{prefix}-federation |
Federation queue |
QUEUE_INTERNAL |
{prefix}-internal |
Internal queue |
QUEUE_EMAIL |
{prefix}-email |
Email queue (consumed by email-sender) |
QUEUE_DLQ |
{prefix}-federation-dlq |
Dead letter queue |
| Variable | Path | Description |
|---|---|---|
MAIN_DIR |
siliconbeest/ |
Unified worker + Vue frontend |
CONSUMER_DIR |
siliconbeest-queue-consumer/ |
Queue consumer worker |
EMAIL_DIR |
siliconbeest-email-sender/ |
Email sender worker |
Option 1: Environment variable (one-off)
PROJECT_PREFIX=myserver ./scripts/setup.shOption 2: Persistent config file
cp scripts/config.env.example scripts/config.env
# Edit config.env with your preferred namesOption 3: Override individual names
export D1_DATABASE_NAME=my-custom-db
export R2_BUCKET_NAME=my-media-bucket
./scripts/deploy.sh --domain social.example.comSKIP_SIGNATURE_VERIFICATION is also available as an explicit true/false
operator setting in scripts/config.env. It defaults to false, and
sync-config.sh --apply writes the same value to both federation workers.
Keep it false in production; true disables inbound HTTP signature
verification and is intended only for controlled testing or troubleshooting.
| Script | Description |
|---|---|
config.sh |
Shared configuration (sourced by all scripts) |
setup.sh |
Interactive first-time setup |
deploy.sh |
Deploy all workers |
update.sh |
Pull, test, migrate, and redeploy |
configure-domain.sh |
Set up custom domain for the unified worker |
generate-vapid-keys.sh |
Generate VAPID key pair for Web Push |
seed-admin.sh |
Create an admin user account |
migrate.sh |
Apply D1 database migrations |
backup.sh |
Backup D1 database and R2 objects |
delete-account.sh |
AP-compliant account deletion |
sync-config.sh |
Sync Cloudflare resource IDs to wrangler.jsonc |
Interactive first-time setup. Creates all Cloudflare resources, generates cryptographic keys, configures secrets, applies migrations, and seeds an admin user.
./scripts/setup.shPrompts for:
- Project prefix (default:
siliconbeest) -- determines all resource names - Instance domain (e.g.
social.example.com) - Instance title
- Registration mode (open / approval / closed)
- Admin email, username, password
- Sentry DSN (optional)
What it does:
- Creates D1 database, R2 bucket, KV namespaces (CACHE, SESSIONS, FEDIFY_KV), Queues
- Generates VAPID key pair (ECDSA P-256), OTP encryption key, and first-run setup secret
- Updates
siliconbeest/wrangler.jsoncwith resource IDs - Sets OTP_ENCRYPTION_KEY and SETUP_SECRET secrets via
wrangler secret put - Stores VAPID keys in D1 settings table
- Applies D1 migrations
- Creates admin user
- Writes
siliconbeest/.env
Build and deploy all 3 workers. Optionally configures custom domain.
# Deploy with custom domain
./scripts/deploy.sh --domain social.example.com
# Deploy to workers.dev subdomains
./scripts/deploy.sh
# Preview without deploying
./scripts/deploy.sh --dry-run
# Skip migrations
./scripts/deploy.sh --skip-migrations| Flag | Description |
|---|---|
--domain <domain> |
Configure custom domain for unified worker |
--dry-run |
Show what would be deployed |
--skip-migrations |
Skip D1 migration step |
The unified worker handles all routes (API + frontend) via a single custom_domain binding.
Production update workflow: pull latest code, validate, migrate, and deploy.
# Standard update
./scripts/update.sh
# Update from a specific branch
./scripts/update.sh --branch release/v0.2.0
# Dry run (check everything, don't deploy)
./scripts/update.sh --dry-run
# Skip tests for hotfixes
./scripts/update.sh --skip-tests| Flag | Description |
|---|---|
--branch <name> |
Git branch to pull (default: main) |
--skip-pull |
Skip git pull, use current working tree |
--skip-tests |
Skip test step |
--dry-run |
Run all checks without deploying |
Steps performed:
git pull(shows changelog)pnpm installfor all projects- TypeScript type check (vue-tsc for unified worker, tsc for others)
- Run tests
- Apply D1 migrations
- Build Vue frontend and deploy unified worker
- Deploy queue consumer and email sender
If any step fails (type errors, test failures, migration errors), the script stops immediately and does not deploy.
Configure custom domain for the unified worker.
./scripts/configure-domain.sh social.example.comUpdates INSTANCE_DOMAIN and the custom_domain route pattern in siliconbeest/wrangler.jsonc, then rebuilds and redeploys.
Generate ECDSA P-256 key pair for Web Push (VAPID).
# Print keys to stdout
./scripts/generate-vapid-keys.sh
# Generate and store in D1 database
./scripts/generate-vapid-keys.sh --store-in-dbVAPID keys are stored in the D1 settings table, not as environment secrets.
Create an admin user account in the D1 database.
# With arguments
./scripts/seed-admin.sh admin@example.com admin MyPassword123
# Interactive (prompts for input)
./scripts/seed-admin.shApply pending D1 database migrations. Migrations are located at siliconbeest/migrations/.
./scripts/migrate.sh --local # Local development
./scripts/migrate.sh --remote # Production (default)
./scripts/migrate.sh --dry-run # List pending without applyingTo create a new migration:
touch siliconbeest/migrations/0003_my_change.sql
# Write SQL, then:
./scripts/migrate.sh --local # Test locally
./scripts/migrate.sh --remote # Apply to productionBackup D1 database tables and R2 object listing.
./scripts/backup.sh # Full backup (D1 + R2)
./scripts/backup.sh --skip-r2 # D1 only
./scripts/backup.sh --output-dir /backupsBackups are saved to ./backups/{timestamp}/.
ActivityPub-compliant account deletion. Sends a Delete(Actor) activity to ALL known federated servers, then removes the account from the local database.
This is destructive and irreversible.
# Dry run (shows what would happen)
./scripts/delete-account.sh <username>
# Actually execute
./scripts/delete-account.sh <username> --confirm
# Delete ALL accounts (server shutdown)
./scripts/delete-account.sh --all --confirmCloudflare's Bot Fight Mode and Super Bot Fight Mode block ActivityPub federation traffic -- other Fediverse servers appear as "bots" and receive 403 responses on /users/* and /inbox.
You MUST create a WAF exception rule:
- Go to Security > WAF > Custom Rules in the Cloudflare Dashboard
- Create a Skip rule with this expression:
This only bypasses requests with ActivityPub content types (
(any(http.request.headers["accept"][*] contains "application/activity+json") or any(http.request.headers["accept"][*] contains "application/ld+json") or any(http.request.headers["content-type"][*] contains "application/activity+json") or any(http.request.headers["content-type"][*] contains "application/ld+json")) and (http.request.uri.path matches "^/users/.*" or http.request.uri.path eq "/inbox" or http.request.uri.path eq "/actor" or http.request.uri.path matches "^/nodeinfo/.*" or http.request.uri.path matches "^/.well-known/.*")application/activity+json,application/ld+json) on federation endpoints -- normal browser traffic is still protected by bot rules. - Action: Skip -- check All remaining custom rules + Super Bot Fight Mode
- Place it FIRST in your rule list (highest priority)
Verify:
curl -H 'Accept: application/activity+json' https://your-domain.com/users/admin
# Should return JSON, NOT an HTML challenge pageWithout this rule, federation is completely broken -- no remote server can discover or interact with your instance.
The unified architecture requires these wrangler secrets:
# OTP encryption key (for 2FA)
wrangler secret put OTP_ENCRYPTION_KEY --name siliconbeest
# First-run setup secret (required by /api/v1/setup)
wrangler secret put SETUP_SECRET --name siliconbeestOptional secrets:
# Sentry DSN for worker error reporting (Sentry is disabled when unset)
wrangler secret put SENTRY_DSN --name siliconbeestVAPID keys are stored in the D1 settings table (not env secrets).
./scripts/generate-vapid-keys.sh --store-in-db
# NOTE: This invalidates all existing Web Push subscriptionsFederation messages that exhaust their retries go to the DLQ. The queue consumer reprocesses each dead-lettered message once more and parks persistent failures into the federation_dlq_parked D1 table. Inspect, replay, or discard parked messages via the admin API (/api/v1/admin/federation/dlq), or view the raw queue in the Cloudflare dashboard (Queues tab).
# WARNING: Invalidates all existing 2FA enrollments
openssl rand -hex 32 | wrangler secret put OTP_ENCRYPTION_KEY --name siliconbeest
# Generate/rotate the first-run setup secret
openssl rand -hex 32 | wrangler secret put SETUP_SECRET --name siliconbeestFetches resource IDs (D1, KV, R2, Queues) from your Cloudflare account and regenerates all wrangler.jsonc files with correct values.
Use when:
- You cloned the repo on a new machine
- Your
wrangler.jsoncfiles are out of date or corrupted - You switched Cloudflare accounts
- Resource IDs changed after recreation
# Dry run -- shows what would change, no files modified
./scripts/sync-config.sh
# Apply -- regenerates all wrangler.jsonc files
./scripts/sync-config.sh --applyWhat it does:
- Verifies
wranglerCLI authentication - Looks up D1 database ID by name
- Looks up KV namespace IDs by title
- Verifies R2 bucket existence
- Reads existing domain/title/registration from current config
- Regenerates
siliconbeest/wrangler.jsonc,siliconbeest-queue-consumer/wrangler.jsonc, andsiliconbeest-email-sender/wrangler.jsonc
Prerequisites: wrangler CLI authenticated (pnpm exec wrangler login)