Two problems with the backend deploy, fixed together:
1. It errored on every main push with 'missing server host' because
BACKEND_VPS_* wasn't set. A permanently-red job is a broken window —
it trains people to ignore deploy alerts and hides real failures.
Add a backend-cfg check job; deploy-backend now runs only when
BACKEND_VPS_HOST is set and skips cleanly (neutral) otherwise,
activating automatically once the host is provisioned.
2. It required a hand-placed /opt/pwap-indexer/.env on the host — a
fragile out-of-band manual step. The deployed image runs only the
indexer (src/index.js), which needs just WS_ENDPOINT beyond the fixed
PORT/DB_PATH (no KYC/council service, so no seed/Supabase secret).
Inject WS_ENDPOINT from the workflow (INDEXER_WS_ENDPOINT secret with a
public default) via -e; drop the --env-file dependency. Config now lives
in the pipeline, not a manual file on the box.
Adds a deploy-supabase job to the quality gate so the self-hosted Supabase
side ships the same way as the frontend/backend instead of by hand:
- gated by the existing telegram-gate (owner approves once, in Telegram)
- runs on main-push in parallel with deploy-app, so edge functions, DB
migrations and the new frontend go live in one post-approval window
- rsyncs the edge-function dirs into the edge-runtime volume (hot-reloaded,
no restart; --no-delete preserves the telegram-* functions)
- applies pending migrations via deploy/apply-migrations.sh: idempotent and
transactional, tracked in supabase_migrations.schema_migrations, skips
non-versioned files (COMBINED_*), never half-records a failed migration
- post-deploy health check verifies the fund-custody guards took effect
(anon lost EXECUTE on the escrow/withdraw RPCs; both freeze triggers exist)
- Telegram alert on success/failure
Requires repo secrets SUPABASE_VPS_HOST/USER/SSH_KEY/PORT.
Closes the "backend has zero CI + manual SSH deploy" audit finding for the indexer.
Part B — CI-runnable tests (the integration-tests/*.live.test.js need a live chain and
are excluded, not stubbed): extract injectable cores (indexer.js: DB+HTTP+decode, no
@pezkuwi import; council.js: createApp factory for the council/KYC routes). New node:test
suite (19/19, fully offline) — in-memory sqlite, dependency-injected chain stub, an
in-memory fake-supabase, and REAL @pezkuwi keyring signatures proving 401 bad-sig /
400 msg-mismatch / 200 valid / 409 dup / 403 non-member / threshold auto-execute. Backend
job now runs npm ci + npm test and is in ci-gate (failing tests block merge). Fixed a
latent bug: block indexing now awaits each insert (was fire-and-forget forEach). Added
runtime deps server.js imported but were missing from package.json (@supabase/supabase-js,
pino, pino-http) — server.js could not have `npm ci`-run before.
Part C — Dockerfile (non-root, HEALTHCHECK /health, sqlite state on a /data VOLUME kept
out of the image) + backend deploy pipeline mirroring the web one: build+push to GHCR,
cosign keyless sign + verify, ssh deploy with the DB volume preserved across deploys,
/health poll, auto-rollback to previous SHA, same Telegram CEO approval gate, main/tags
only, never fork PRs. New secrets documented in backend/DEPLOY.md (BACKEND_VPS_HOST/USER/
SSH_KEY). DB_PATH env (default ./transactions.db) lets prod point at the volume.
Note for owner: if server.js (council/KYC bootstrap) is legacy/unused, its newly-added
deps can be dropped instead.
Audit remediation (build/supply-chain/quality):
- Add `@pezkuwi/api-augment` import in main.tsx -> tsc errors 416->328 (Codec-typed
on-chain reads now augmented); add `typecheck` script + a non-blocking Typecheck CI
step (flip to blocking once errors reach 0).
- CI: `npm install` -> `npm ci` (lockfile enforced) in web + security-audit jobs; add a
Backend Indexer job (npm ci + node --check + non-blocking high/critical audit). No
live tests run in CI (they need a live chain) — a mockable CI test subset is a
follow-up.
- Vulns: bump dompurify ^3.4.12 + postcss ^8.5.23 (web prod highs cleared; only the
react-router v7 open-redirect remains, deferred as a breaking major); backend tar
^7.5.22 + ws ^8.21.1 (clears the critical tar advisory). Lockfiles regenerated.
- Prod build now drops console/debugger (vite esbuild.drop); NotificationBell realtime
subscription now returns cleanup + uses a per-user channel (fixes leak); ProfileSettings
language codes aligned to i18n config (ku-kurmanji/ku-sorani).
- Root package.json sdk-ui path env-driven (${SDK_UI_DIR}); generate-docs rustup path
resolved dynamically; Docker image license label MIT (matches LICENSE). Removed scratch
files (mimari.txt, _scratch_credit_serok.mjs).
The security-audit gate ran 'npm audit --audit-level=high' over all deps,
so newly-published advisories on build-only tooling (esbuild, elliptic via
vite-plugin-node-polyfills, etc.) repeatedly blocked production deploys
even though that code ships to no user. Scope the gate to production
dependencies with --omit=dev. Verified: 'npm audit --audit-level=high
--omit=dev' → 0 vulnerabilities. TruffleHog secret scanning is unchanged.
docker/login-action writes ~/.docker/config.json but cosign on self-
hosted runner does not always read it. Add 'cosign login ghcr.io'
before sign (build-image) and verify (deploy-app, deploy-pex) so the
registry blob upload/download authenticates correctly.
The previous run signed via Sigstore (Fulcio cert + Rekor tlog entry
created) but failed at the final 'push signature blob to GHCR' step
with UNAUTHORIZED. Explicit cosign login solves this.
* Faz 3.1 — All CI jobs moved to self-hosted pwap-runner (DEV VPS).
No more dependency on GitHub-hosted runners — supply-chain attack
surface from GHA runner image compromise eliminated.
* Faz 3.3 — Automatic rollback on health-check fail. Each deploy stamps
/.deploy-sha into the artifact. On health-check failure, the deploy
job reads the previous SHA from the live site, pulls that image, and
redeploys. Telegram notification differentiates: rolled-back-OK,
rollback-also-failed, no-prev-available, manual-rollback-needed.
* E.3 — cosign keyless image signing. build-image signs the GHCR
manifest via Sigstore Fulcio (OIDC, no long-lived keys). deploy-app
and deploy-pex verify the signature before extracting /dist —
unsigned or tampered images cannot deploy. Identity-pinned to this
workflow file.
* E.5 — Subresource Integrity (SRI). vite-plugin-subresource-integrity
injects sha384 integrity= into <script>/<link> tags at build time.
CDN/proxy compromise cannot inject tampered JS — browser blocks on
hash mismatch.
* E.2 — Dependabot triage. 14 alerts: 7 high + 4 moderate cleared via
npm audit fix + npm overrides (elliptic, create-ecdh). 6 low
(transitive in vite-plugin-node-polyfills chain) accepted; the
upstream fix proposes a semver-major DOWNGRADE which makes no sense.
* E.1 — Branch protection on main: CI Gate ✅ required, 1 review
required, force-push and deletion blocked.
Vite aliases @pezkuwi/utils → ../shared/utils, so the Docker build context
must include both web/ and shared/. Previous context: ./web missed shared/
which caused 'Could not load /shared/utils/formatting' at module resolution.
Changes:
- Dockerfile WORKDIR=/build/web; COPY web/* and shared/* explicitly
- Workflow context: ./ + file: ./web/Dockerfile
- Move .dockerignore from web/ to pwap root (matches new context)
Faz 1 — State-actor threat-model defenses:
* Telegram approval gate via PEXSEC_BOT — CEO must approve every deploy in Telegram (30-min timeout). Runs on new self-hosted pwap-runner on DEV VPS, shares /tmp/pexsec-gates/ with pexsec-bot.service.
* DEV VPS app-deploy user privilege drop — deploys no longer run as root. CI key restricted with no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-user-rc. Privilege drop verified (cannot read /etc/shadow, /root/, sudo blocked).
* Image-based deploy — Dockerfile (node 20 build → busybox:musl dist) pushed to GHCR with SHA tag. Deploys pull image, extract /dist, scp to VPS. Immutable artifacts, full provenance.
* Health check + Telegram failure alert post-deploy.
* Rollback path: workflow_dispatch with rollback_to=<sha> — skips build, redeploys old image. CEO gate still required.
Faz 2 — Higher-tier defenses:
* TruffleHog secret scan — PR diff (fast) + push full-repo (verified secrets only).
* CodeQL SAST workflow — javascript-typescript, security-extended + security-and-quality queries. PR + push + weekly cron.
* npm audit raised from --audit-level=critical to --audit-level=high (caught more CVEs).
* CI Gate ✅ explicit merge-block job — fails if any required check is not success/skipped.
Enables manual re-deploy via 'gh workflow run quality-gate.yml' without
needing a code push. Useful for: redeploy after secret rotation, post-
incident recovery, deploy verification.
Split monolithic deploy job into bump-version + deploy-app + deploy-pex.
Both deploys run in parallel from same build artifact, independent
secrets per VPS. If one country blocks a domain, the other VPS keeps
serving the same version.
- bump-version: single source of version bump, runs before both deploys
- deploy-app: existing target /var/www/subdomains/app on DEV VPS
- deploy-pex: new target /var/www/pex.mom on VPS3 (217.77.6.126)
Requires secrets: VPS_PEX_HOST, VPS_PEX_USER, VPS_PEX_SSH_KEY, VPS_PEX_SSH_PORT
* chore: update exchange submodule to pex.network release + add shared images
Exchange submodule advanced to include:
- sweeper.js: TRC-20 JWT Bearer auth, DOT transferAll, PEZ-AH pre-fund
- docker-compose.yml: pex.network defaults for VITE_API_BASE_URL and SMTP_FROM
- .github/workflows/build-deploy.yml: pex.network build arg for web service
Shared images added: keziyakurd, kiwi_perwerde, kurdistan_assembly, pezkuwi, satoshi_qazi_muh
* chore: remove mobile/ from monorepo, suspend CI mobile job
Mobile app moved to /home/mamostehp/pwap-mobile (local, suspended).
Will be re-integrated when mobile development resumes.
- Removed mobile/ directory entirely
- Removed Mobile App job from quality-gate.yml so CI no longer blocks
Mobile app is distributed via Play Store DApps browser, no longer
built/deployed from this repo. Deploy and security audit now only
depend on the web job passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add VITE_NETWORK and endpoint env vars to CI/CD build
- Add Asset Hub and People Chain endpoints to build config
- Add hardcoded production fallbacks in PezkuwiContext
- Disable WebSocket real-time service (not deployed yet)
- Endpoints: rpc.pezkuwichain.io, mainnet, beta, asset-hub, people
Features:
- Add XCMTeleportModal for cross-chain HEZ transfers
- Support Asset Hub and People Chain teleports
- Add "Fund Fees" button with user-friendly tooltips
- Use correct XCM V3 format with teyrchain junction
Fixes:
- Fix PEZ transfer to use Asset Hub API
- Silence unnecessary pallet availability warnings
- Fix transaction loading performance (10 blocks limit)
- Remove Supabase admin_roles dependency
CI/CD:
- Add auto-deploy to VPS on main branch push
- Add version bumping on deploy
- Upload build artifacts for deployment
The workflow was using npm workspace commands (-w web) but the repository
doesn't have a root package.json configured for workspaces. Updated to use
working-directory instead to properly execute commands in the web directory.
Changes:
- Install dependencies: npm ci in web directory
- Run linter: npm run lint in web directory
- Run tests: npm run test in web directory
- Build project: npm run build in web directory
This fixes the "No workspaces found" error that was causing the workflow to fail.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Refactor security-check.yml to separate critical vs optional checks
- Make TruffleHog, Gitleaks, and Snyk scans optional (continue-on-error)
- Fix .gitattributes negative pattern warning (!.env.example)
- Use specific .env patterns instead of wildcards
- Improve workflow job organization and summary reporting
This ensures the CI/CD pipeline doesn't fail when optional security
tools are not configured with tokens, while maintaining strict
validation for critical security checks.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>