Edgeweir
Reference

Command line

deploy.sh, the node installer install.sh, Docker Compose operations, the container entrypoint, account recovery, and development commands.

deploy.sh

Installer and operations script for 宝塔 / aaPanel Compose deployments. Usage: ./deploy.sh <command>.

CommandEffect
installInteractive install: choose the database mode, write .env, start
update [tag]Back up, then upgrade to the latest version or the given tag; --no-backup skips the backup. Alias upgrade
backupBack up the database, .env (without the master key), and the Compose file to backups/, keeping the newest 5
restore <backup>Back up the current database, then replace it with the backup's edgeweir.dump; .env stays, --no-backup skips the backup
configChange the console URL and the node channel URL
start, stopStart (applying .env changes), stop
restartRecreate the console container and start, applying .env changes
statusContainer status and the running version
logs [service]Follow logs; service is console or postgres
setup-tokenPrint the first-run setup token
template <host|bundled>Print a Compose template for pasting into the panel
self-updateReplace the script with the deploy.sh shipped in the current image, or the GitHub version when the image has none
helpPrint usage

Arguments, unattended variables, files written, backup layout, and exit behavior: deploy.sh reference.

Node installer

The console serves install.sh at /install.sh; it installs and enrolls edgeweir-node on a node. The install command generated by the console already carries --server and --ca-sha256; see Adding nodes.

Node
export EDGEWEIR_TOKEN='<one-time token>'
curl -fsSL https://<console>/install.sh | sudo --preserve-env=EDGEWEIR_TOKEN bash -s -- \
  --server https://<console>:8443 --ca-sha256 <CA fingerprint>

Options

<console> is EDGEWEIR_PUBLIC_URL, written into the script when the console serves it.

OptionArgumentDefaultEffect
--serverURLRequiredNode channel URL; must be https://.
--ca-sha256HEXRequiredSHA-256 fingerprint of the console's node CA, 64 lowercase hex characters; pinned during enrollment.
--token-filePATHNoneRead the enrollment token from a file (whitespace removed); takes precedence over EDGEWEIR_TOKEN.
--versionVERlatestedgeweir-node version to install; a semantic version, optional v prefix. latest is resolved from the mirror's latest file, then the latest GitHub release. Without it (and without --force), an enrolled host downloads and installs nothing; the service is only started and checked.
--formatauto|deb|rpm|tarautoPackage format. auto: deb with dpkg and apt-get; rpm with rpm and dnf or yum; otherwise tar.
--mirrorURL<console>/downloads/edgeweir-nodeedgeweir-node release mirror, with files at URL/latest and URL/v<version>/<file>; cosign comes from the sibling cosign/v<version>/. Each file is tried from the mirror first, then from GitHub.
--mirror-onlyNoneOffNever fall back to GitHub.
--no-modsecurityNoneOffDo not install edgeweir-openresty-modsecurity: the node does not support OWASP CRS.
--no-startNoneOffInstall and enroll only: do not require systemd, do not enable or start the service, no health check.
--forceNoneOffEnroll an enrolled host again (needs a new token): stops edgeweir-node, replaces its identity, and starts it again.
--allow-unsignedNoneOffSkip the cosign signature check; development only. SHA-256 is still verified.
-h, --helpNoneNonePrint usage.
Environment variableEffect
EDGEWEIR_TOKENOne-time enrollment token, format ewt_…. The script removes it from its environment after reading, so other child processes do not inherit it; it reaches edgeweir-node enroll through the environment.

--token and --token=… are rejected: command-line arguments are visible in the process list.

Verification

ObjectCheck
checksums.txtKeyless cosign signature (checksums.txt.sigstore.json). Certificate identity: https://github.com/marvinli001/edgeweir-node/.github/workflows/release.yml@refs/tags/v<version>; OIDC issuer: https://token.actions.githubusercontent.com
PackageSHA-256 against the signed checksums.txt
cosignWithout cosign on the machine, v3.1.3 is downloaded and checked against the SHA-256 pinned in the script

Nothing downloaded runs before these checks pass. System requirements, the full flow, and installed files: Adding nodes.

Exit codes

CodeMeaning
0Installed and enrolled, and edgeweir-node healthcheck passed (not checked with --no-start)
1A check or step failed, including a health check that did not pass within 90 seconds; stderr shows [edgeweir] error: and the reason
2--server or --ca-sha256 missing, or -h/--help given

The script neither upgrades nor uninstalls. Node upgrades: Node upgrades.

Docker Compose

Run in the directory of compose.yml; add -f <file> for other Compose files.

OperationCommand
Start, or apply .env changesdocker compose up -d
Build from source and startdocker compose up -d --build
Stopdocker compose stop
Remove containers and networks, keep volumesdocker compose down
Restart (does not apply .env changes)docker compose restart console
Statusdocker compose ps
Follow logsdocker compose logs -f console
Show the setup token log linedocker compose logs console | grep setupToken
Health check (host)curl -s http://127.0.0.1:3000/healthz
Health check (in the container)docker compose exec console edgeweir-healthcheck
Recover the account (reset the password, turn two-factor off)docker compose exec console node dist/server/recover.js --reset-password --disable-two-factor, see Account recovery
Pull the image set by EDGEWEIR_VERSION and recreatedocker compose pull && docker compose up -d
Start ClickHouseCOMPOSE_PROFILES=analytics in .env, then docker compose up -d (later commands include it)

docker compose down -v removes named volumes such as postgres-data, that is, all data.

Print only the setup token:

docker compose logs --no-color --no-log-prefix console \
  | sed -n 's/.*"setupToken":"\([^"]*\)".*/\1/p' | tail -n 1

/healthz returns {"status":"ok","version":"<version>"}. When EDGEWEIR_HTTP_PORT is not 3000, use that port. Upgrade and rollback steps: Versions, upgrades, and rollback.

Container

ItemValue
Imageghcr.io/marvinli001/edgeweir:<tag>, linux/amd64 and linux/arm64
Entrypoint/sbin/tini --
Commandnode --enable-source-maps dist/server/main.js, working directory /app
Usernode
Ports3000 (web, API), 8443 (node channel)
Health checkedgeweir-healthcheck; interval 10 s, timeout 3 s, start period 30 s, 5 retries
Bundled files/usr/local/bin/edgeweir-certd, /usr/local/bin/edgeweir-healthcheck, /app/deploy.sh
Stop signalsSIGTERM, SIGINT: close the HTTP and node channel listeners and end the nodes' watch streams, give requests in flight up to 3 seconds and pg-boss up to 5 seconds at the same time, exit with 0; after 8 seconds exit with 1

edgeweir-healthcheck: with ROLE=worker it returns 0; other roles request http://127.0.0.1:${PORT}/healthz with a 2-second timeout and return non-zero on failure.

Environment defaults of the image: Environment variables.

ROLE

Values: all (default), app, and worker. Components, listeners, and scaling per role: Deployment overview.

Account recovery

dist/server/recover.js resets the password of the console's only account and turns two-factor authentication off, for when nobody can sign in. It reads the same environment variables as the console (DATABASE_URL, EDGEWEIR_MASTER_KEY, and so on) and changes the database directly, without the web UI or HTTP. Steps: Account recovery.

docker compose exec console node dist/server/recover.js --reset-password --disable-two-factor
DeploymentCommand
Docker Composedocker compose exec console node dist/server/recover.js <options>
Docker Compose, console container not runningdocker compose run --rm console node dist/server/recover.js <options>
宝塔 / aaPanel (deploy.sh)docker exec -it edgeweir-console node dist/server/recover.js <options>
Source (pnpm dev)pnpm --filter @edgeweir/console recover <options>, see Commands

Options

OptionEffect
--reset-passwordSet a new password, 12–128 characters
--disable-two-factorTurn two-factor authentication off; delete the TOTP secret and backup codes
-h, --helpPrint usage

At least one of --reset-password and --disable-two-factor is required.

New password

Standard inputHow it is read
Terminal (allocated by default by docker compose exec and docker exec -it)Prompts New password: and Repeat new password: without echo; nothing changes when the two entries differ. Ctrl-C cancels
Pipe or fileThe first line, without its \n or \r\n ending
docker compose exec -T console node dist/server/recover.js --reset-password < new-password.txt

-T allocates no terminal; use it only with a pipe or file, because a password typed at the terminal is then shown on screen. The password is never taken from a command-line argument or an environment variable: arguments are visible in the process list and the shell history.

Result

These changes commit in one transaction with the audit entry; if any step fails, nothing changes:

ChangeDescription
PasswordHashed by better-auth (scrypt) and stored on the account's password credential
Two-factor authenticationOff; the TOTP secret and backup codes are deleted
SessionsAll sessions of the account are deleted, as are sign-ins waiting for their two-factor code and trusted-device records
Audit logaccount.recover, actor System (name recover); metadata passwordReset, twoFactorDisabled, sessionsRevoked

Name, email, passkeys, and AccessKeys do not change. Standard output shows the account's name and email and what changed, never the password, its hash, or a session token:

Account: Ops <admin@example.com>
Password reset.
Two-factor authentication turned off.
Signed out 2 sessions.

Exit codes

Exit codeMeaning
0Done, or -h / --help
1Nothing changed: no account (setup not completed), password length out of range, entries differ, cancelled, invalid configuration, or the database connection or write failed; stderr shows error: and the reason
2Unknown option, extra argument, or no action given; stderr shows the usage

Development

Requirements: Node.js 24.11.0 or later, pnpm 12.6.0, Docker; helpers/certd and pnpm e2e also need Go 1.27.1. buf is installed with the dev dependencies.

Run locally

  1. Install dependencies.

    pnpm install
  2. Start the development database (compose.dev.yml; port: DEV_POSTGRES_PORT).

    docker compose -f compose.dev.yml up -d
  3. Create .env and set EDGEWEIR_MASTER_KEY (openssl rand -base64 32). The template's DATABASE_URL matches the development database.

    cp .env.example .env
  4. Start the console.

    pnpm dev
  5. Verify.

    curl -s http://localhost:3000/healthz

    Expected output: {"status":"ok","version":"dev"}.

Certificate issuance needs the certificate helper: cd helpers/certd && go build -o bin/edgeweir-certd ., then set its absolute path in EDGEWEIR_CERTD_BIN.

Commands

Run from the repository root.

CommandEffect
pnpm devOne process runs the API, the node channel, and Vite HMR; loads the repository .env when present; web listens on PORT
pnpm buildTurborepo build of all packages; the console goes to apps/console/dist
pnpm typecheckType checking
pnpm testVitest; the database is PGlite, no Docker needed
pnpm lintbiome check . and buf lint proto
pnpm formatbiome check --write .
pnpm proto:lintbuf lint proto
pnpm proto:genGenerate packages/proto/src/gen from proto/
pnpm db:generateGenerate an SQL migration in packages/db/migrations from packages/db/src/schema (drizzle-kit)
pnpm e2eRun scripts/e2e.sh
pnpm --filter @edgeweir/console startRun the built dist/server/main.js
pnpm --filter @edgeweir/console recover <options>Run the account recovery command from source; reads .env in the repository root when present. No -- before the options
pnpm --filter @edgeweir/console test:e2ePlaywright tests

End-to-end tests

scripts/e2e.sh runs against compose.e2e.yml. It needs curl, jq, docker, and node; the installer step also needs goreleaser v2, syft, Go 1.27.1, and the sibling edgeweir-node checkout, and the signed upgrade step needs cosign v3.1.3; the run needs access to deb.debian.org and openresty.org.

docker compose -f compose.e2e.yml up -d --build
pnpm e2e
ArgumentEffect
--upRun docker compose -f compose.e2e.yml up -d --build first
--downRun docker compose -f compose.e2e.yml --profile '*' down -v at the end, removing the profile services (ClickHouse, upgrade test node, regional probes) and their volumes as well
--skip-uiSkip the Playwright browser tests

Arguments follow the script: bash scripts/e2e.sh --up --down.

VariableDefaultEffect
COMPOSE_PROJECT_NAMEedgeweir-e2eCompose project name
E2E_CONSOLE_PORT13000Console host port
E2E_NODE_PORT18080Node HTTP host port
E2E_NODE_TLS_PORT18443Node HTTPS host port (TCP and UDP)
E2E_TAGe2eTag of the built images
E2E_SUBNET172.28.213.0/24Default network subnet; the script adds it to the origin allow list
E2E_ISOLATED_SUBNET172.28.214.0/24Isolated network subnet; stays outside the allow list
E2E_INSTALL_IMAGEdebian:bookworm-slim@sha256:…Clean system image for the install.sh test
E2E_ANALYTICSliteThe console's EDGEWEIR_ANALYTICS
E2E_CLICKHOUSE_PORT19123ClickHouse HTTP port, bound to 127.0.0.1
E2E_ACME_PORT14000Pebble ACME port, bound to 127.0.0.1
E2E_ACME_MGMT_PORT15000Pebble management port, bound to 127.0.0.1
E2E_MOCK_PORT19090Mock services port
EDGEWEIR_NODE_CONTEXT../edgeweir-nodeedgeweir-node source directory

For a second environment on the same machine, change COMPOSE_PROJECT_NAME, the ports, E2E_TAG, E2E_SUBNET, and E2E_ISOLATED_SUBNET.

Edit on GitHub

On this page