Commit Graph

927 Commits

Author SHA1 Message Date
SatoshiQaziMuhammed e95907ec87 Merge pull request #24 from pezkuwichain/fix/function-authorization
fix(security): identify the caller from their token, not the request body
2026-07-30 04:25:26 -07:00
pezkuwichain 0fe8c85dfe fix(security): identify the caller from their token, not the request body
two-factor-auth and notifications-manager run with the service role, so they
bypass RLS and whatever user id they act on is the entire authorisation
decision. Both read that id from the request body, and neither checked who was
calling. Access-Control-Allow-Origin was '*' on both.

Confirmed against the live endpoint: with only the public anon key and no user
session, from an arbitrary origin,

  POST /functions/v1/two-factor-auth {"action":"check","userId":"<any-uuid>"}
  -> 200 {"success":true,"enabled":false}

VERIFY_JWT is on and does not stop this, because the anon key is itself a valid
JWT and every browser has it. The same path reaches "disable", so anyone could
turn off another account's 2FA, and read, delete or forge their notifications.

The client already sends the signed-in user's token — functions.invoke puts the
session access token in the Authorization header — so the caller can simply be
read from it. Both functions now do that and ignore the body's userId. An
anon-key call carries no user, so it is rejected with 401.

create is the one action that may target someone else, because AdminPanel
notifies other users; that path now requires admin or super_admin in
admin_roles. CORS drops to the same allowlist the other functions here use.

No frontend change: the body may keep sending userId, it is simply not read.

Note: 2FA is not yet enforced at login — TwoFactorVerify is not referenced
anywhere and Login.tsx never checks it, so enrolling does not currently protect
sign-in. Tracked separately; this change is about the endpoints themselves.
2026-07-30 04:05:03 -07:00
SatoshiQaziMuhammed 46669aa72d Merge pull request #23 from pezkuwichain/feat/adopt-cloud-functions
feat(supabase): adopt the three functions left behind on the cloud project
2026-07-30 02:40:23 -07:00
pezkuwichain dc8533d707 feat(supabase): adopt two-factor-auth, notifications-manager, email-verification
The web app calls these three from TwoFactorSetup, TwoFactorVerify,
NotificationBell, AdminPanel and EmailVerification, but this repo never defined
them and they are not deployed to the self-hosted host the app points at. They
were left behind on the cloud project when everything else moved, so every one
of those calls has been hitting a function that is not there — two-factor auth,
notifications and email verification have all been silently dead.

Recovered from the deployed bundles on the cloud project and added here so the
source is tracked and CI deploys them alongside the rest. The tables they need
(two_factor_auth, notifications, activity_logs, profiles,
email_verification_tokens) already exist on the self-hosted database, so this is
a code move only.

Registry on the host now lists all three under this project.

NOTE: two-factor-auth and notifications-manager take userId straight from the
request body while running with the service role, and neither checks the caller.
That is how they run on the cloud project today, so this change does not make it
worse, but it does mean anyone can disable another account's 2FA or read and
delete their notifications. Fixing it properly needs an admin-role check
alongside the JWT, because AdminPanel legitimately creates notifications for
other users. Tracked separately rather than folded into a move.
2026-07-30 02:17:29 -07:00
SatoshiQaziMuhammed 8cc3935b91 Merge pull request #22 from pezkuwichain/ci/backend-deploy-guard
ci(supabase): ownership gate for shared edge functions + complete 6-locale copy
2026-07-30 02:05:16 -07:00
pezkuwichain 9e10eee977 Merge remote-tracking branch 'origin/main' into ci/backend-deploy-guard 2026-07-30 00:45:09 -07:00
pezkuwichain 08fd9d2b34 i18n(ar,ckb,fa,kmr): complete multisig operations and bridge withdrawal copy
Brings the remaining four locales up to the same coverage as en and tr: the 14
multisigOps strings, wallet.multisigOperations, and the corrected withdrawal
wording.

The withdrawal strings were not merely missing here. bridge.withdrawAddress
existed in all four and still described the field as a bank account or crypto
address the user types in, and withdrawSuccess still said USDT would be sent
after a delay. Neither is true: the destination is derived from the connected
account, and burned wUSDT is only released once the multisig signers approve.
Because the keys existed, fallbackLng never applied and speakers of these four
languages were reading incorrect instructions about their own funds.

Terminology follows each file's existing vocabulary rather than inventing new
terms — kmr Îmze/Berîk, ckb واژوو/جزدان, ar توقيع/المحفظة, fa امضا/کیف پول.
The two Kurdish variants still want a native speaker's read before we treat
them as final.
2026-07-29 20:34:46 -07:00
pezkuwichain cff5ae8032 fix(social): point KurdMedia links at the canonical accounts
t.me/pezkuwichain and twitter.com/pezkuwichain are both retired handles. The
canonical set is t.me/kurdishmedya for the channel and x.com/bizinikiwi for X.
2026-07-29 20:26:00 -07:00
pezkuwichain c117b023b1 feat(wallet): surface multisig operations shortcut to bridge signatories
Adds the entry point for the operations screen the multisigOps strings were
written for. The button only renders when the connected account is one of the
five USDT bridge signatories; that check is for discoverability only, the route
stays gated by ProtectedRoute.
2026-07-29 20:26:00 -07:00
pezkuwichain 88f1eb7245 chore(images): drop unused assets, recompress HEZ token logo
react-logo, partial-react-logo, hez-token, wallet and HEZ_Token_Logo_512 have no
remaining references anywhere in the tree (the wallet.png hits in
LandingPageDesktop are /ref-stepN-wallet.png under public/, different files).

HEZ_Token_Logo.png recompressed 5.2 MB -> 3.9 MB, same image.
2026-07-29 20:24:52 -07:00
pezkuwichain ee384064c1 i18n(en,tr): add multisig operations strings, correct bridge withdrawal copy
MultisigOperations.tsx and MultisigOperationsPage.tsx are already in the tree and
read multisigOps.* plus wallet.multisigOperations, but no locale defined them, so
the screen rendered raw key names.

The bridge withdrawal copy was also wrong about what happens: the destination is
derived from the connected account rather than typed in, and burned wUSDT is not
released until the multisig signers approve. Both strings now say that.

ar, ckb, fa and kmr still carry the old withdrawal wording. They are not left to
fall back to English because the key exists there — they need translations from a
native speaker, tracked separately.
2026-07-29 20:24:51 -07:00
pezkuwichain 30cca41d37 ci(supabase): deploy edge functions through the ownership gate
The deploy step rsynced this repo's whole functions tree into the shared
edge-runtime volume. The comment on that line acknowledged the volume was
shared, but additive-without---delete only protects names we do not also use:
this repo and pezkuwi-telegram-miniapp both ship a telegram-auth, so the
2026-06-28 deploy replaced theirs and broke that app's sign-in for a month.

Writes now go through supabase-deploy-functions on the host, which validates
every incoming directory against a registry of who owns which name and refuses
the whole deploy if any of them is not ours. Nothing this repo legitimately
deploys changes; the miniapp has moved its two colliding functions to a tgm-
namespace, so the two projects no longer address the same directories.

Registry: /opt/supabase-self-hosted/functions-registry.json
2026-07-29 20:22:03 -07:00
SatoshiQaziMuhammed bb0b7d4183 Merge pull request #21 from pezkuwichain/ci/backend-deploy-guard
ci: backend indexer deploy — self-contained config + host gate
2026-07-25 21:26:56 -07:00
pezkuwichain 90f94df24b ci: make backend indexer deploy self-contained + config-gated
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.
2026-07-25 21:08:07 -07:00
SatoshiQaziMuhammed 3d786f47d3 Merge pull request #20 from pezkuwichain/audit-remediation
security: P2P fund-custody audit remediation + backend CI/CD (proven-by-execution)
2026-07-25 18:30:22 -07:00
pezkuwichain 85b2c48025 ci: automated Telegram-gated Supabase deploy (edge functions + migrations)
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.
2026-07-25 17:53:31 -07:00
pezkuwichain 193ef34c3e ci: exclude Deno supabase tests from vitest sweep
vitest's default include swept supabase/functions/**/__tests__ and
supabase/migrations/__tests__ — those are Deno tests (Deno.test + pglite)
that only run under `deno test` and fail under node/jsdom. Add supabase/**
to test.exclude so the web test run covers only the Vite app's own suites.
2026-07-25 16:50:13 -07:00
pezkuwichain 5cf55aecbf ci: exclude Deno edge functions and migration tests from web eslint
The web ESLint config (browser/React rules: no-explicit-any, no-unused-vars)
was linting supabase/functions/** and supabase/migrations/** — Deno TypeScript
that legitimately uses `any` for the chain/Supabase surfaces and npm: imports.
These have their own tooling (deno check + deno test) which passes. Add both
dirs to ignores so `eslint .` covers only the Vite app it is configured for.
2026-07-25 16:30:01 -07:00
pezkuwichain 3c7c4192b9 docs(backend): track backend deploy runbook (gitignored *.md override) 2026-07-25 11:02:16 -07:00
pezkuwichain 80d273ff11 ci/cd(backend): offline test suite + Dockerize + audit-grade deploy (b + c)
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.
2026-07-25 11:01:47 -07:00
pezkuwichain 20a5602167 test(p2p): DB-backed proof of financial RLS + escrow REVOKE enforcement
Replicates the Supabase RLS model on a real Postgres engine (pglite) with roles + an
auth.role() JWT-claim stub and proves the harden_financial_rls + escrow-REVOKE
migrations enforce: anon cannot read balances / payment-method PII or execute
release_escrow_internal; the SECURITY DEFINER read path + service_role still work. 6/6.
Together with the freeze-trigger test and the 34 app-auth tests, the fund-custody
security layer is now proven-by-execution (logic); staging deploy is the in-situ step.
2026-07-25 10:20:38 -07:00
pezkuwichain cd9bc577ae test(p2p): DB-backed proof of the trade fund-routing freeze trigger (BYPASS #1)
Runs the exact freeze trigger from migration 20260725040000 against a real Postgres
engine (pglite) and proves: anon UPDATE of buyer_id/crypto_amount (the escrow-redirect
attack) is BLOCKED; status/proof-only stays allowed; service_role is exempt; the
attacker redirect never persists. 5/5 pass. Upgrades the custody-critical trigger from
code-reviewed to proven-by-execution. RLS/REVOKE role-enforcement still needs a
multi-role staging Postgres (pglite is single-user).
2026-07-25 10:15:28 -07:00
pezkuwichain a0492a41ac docs(p2p): track the security deploy runbook (ordered migrations, edge functions, secret names)
Consolidated gated-deploy runbook for the P2P security remediation. Contains no secret
values — only the names of the env vars/secrets the owner sets on staging/prod.
2026-07-25 02:46:40 -07:00
pezkuwichain 25ed29edcb security(p2p): freeze trade/offer fund-routing columns + atomic release (2 verify-found bypasses) + tests
A bounded adversarial re-verification of the new fund-custody auth found two real
bypasses that the fix rounds missed; both fixed here, plus executable proof.

- BYPASS #1 (CRITICAL, external): p2p_fiat_trades/p2p_fiat_offers kept USING(true)
  anon UPDATE, and confirm-payment / admin_resolve_dispute read the fund DESTINATION
  (buyer_id), amount and token straight from those rows. An anon attacker could
  `UPDATE p2p_fiat_trades SET buyer_id=<attacker>` so the honest seller's correctly-
  signed release paid the attacker. Fix: BEFORE UPDATE triggers freeze the fund-routing
  columns (offer_id, seller_id, buyer_id, crypto_amount, fiat_amount, price_per_unit,
  escrow_locked_amount on trades; seller_id, token, amount_crypto on offers) for any
  non-service-role writer — trigger-level, independent of RLS/grants; status/proof/chat
  stay open. (Corrects the earlier triage that dismissed these tables as non-custodial.)
- BYPASS #2 (double-release TOCTOU): confirm-payment checked status then released
  non-atomically, so two concurrent differently-nonced valid requests could both release
  and drain other buyers' escrow. Fix: atomic compare-and-swap of payment_sent->completed
  BEFORE the escrow move (loser gets 409; revert on release failure).
- Hardening: REVOKE EXECUTE on the dead legacy resolve_p2p_dispute mover from
  PUBLIC/anon/authenticated.
- Proof: 34 executable tests (Deno) — identity-auth unit tests (sr25519 sign/verify,
  challenge money-param binding, citizen/visa ownership, nonce single-use+concurrency,
  freshness) + per-handler authorization-flow tests (bad-sig/cross-id/wrong-state/replay,
  CAS double-release). All pass.
2026-07-25 02:44:02 -07:00
pezkuwichain 165cb47c64 security(p2p): close escrow-RPC anon-drain (release/lock behind signed edge functions)
Round 2 — fixes the CRITICAL discovered during round 1 (as severe as the withdrawal
BOLA): lock_/release_/refund_escrow_internal had default PUBLIC EXECUTE and the client
called release_escrow_internal directly with the anon key, so anyone could drain any
victim's LOCKED balance.

- release_escrow_internal now reachable only via new signed confirm-payment edge
  function: verifies seller wallet signature (raw + <Bytes>), asserts the wallet owns
  the seller identity, derived user_id == trade.seller_id, trade == payment_sent,
  single-use nonce, then release runs with the service role. confirmPaymentReceived
  now invokes confirm-payment (no more anon rpc).
- lock_escrow_internal now behind new signed lock-escrow edge function (a caller can
  only lock its own balance; stops griefing a victim's balance). Removed a zero-amount
  no-op lock call.
- Migration 20260725030000: REVOKE EXECUTE on lock_/release_/refund_escrow_internal
  from PUBLIC/anon/authenticated; GRANT to service_role only. refund has no direct
  client caller (service-role + admin_resolve_dispute only).
- DEPLOY_RUNBOOK.md consolidates the ordered migrations, edge functions and secrets
  for both security rounds. Migration 20260725030000 must ship WITH the two new edge
  functions + frontend or offer-create/payment-release break.

Remaining (non-fund, Round 2+ follow-up): read RPCs still key on a non-secret user_id
(binding to a signed session would force a sign-prompt on every passive balance read —
deferred); p2p_fiat_offers/trades/messages retain USING(true) (status labels move no
funds now that all escrow movement is service-role-gated).
2026-07-25 00:21:20 -07:00
pezkuwichain a8f41cd47f security(p2p): fix withdrawal BOLA, financial RLS exposure, admin dispute escrow
Critical/high audit remediation on the custodial P2P ledger. Auth model is
wallet-based; identity (citizen/visa) is cryptographically bound to a wallet
(People Chain tiki.citizenNft / active p2p_visa), which enables a correct fix.

- Withdrawal BOLA (CRITICAL): process-withdraw now requires a wallet-SIGNED
  challenge; server verifies signature (raw + <Bytes> forms), asserts the signer
  OWNS the identity, consumes a single-use nonce (replay), and derives user_id
  server-side — the client-supplied user_id is ignored. process-withdrawal (batch)
  now requires the service-role key (was: any bearer, incl. public anon key).
  request_withdraw is REVOKEd from anon/authenticated + service-role guarded.
- Financial RLS (HIGH): drop the blanket USING(true) SELECT on user_internal_balances,
  p2p_balance_transactions, p2p_deposit_withdraw_requests; lock p2p_user_payment_methods
  (IBAN PII) + p2p_fiat_disputes UPDATE to service_role; legitimate reads move behind
  scoped SECURITY DEFINER RPCs.
- Deposit integrity: verify-deposit now binds the on-chain sender to identity ownership
  before crediting.
- Admin dispute (CRITICAL fund-logic): DisputeResolutionPanel relabeled trades without
  moving escrow. New admin-signed resolve-dispute function + admin_resolve_dispute RPC
  moves escrow (release/refund/split) atomically with correct accounting (avoids the
  double-count in the legacy resolve_p2p_dispute). Client isAdmin documented as cosmetic.

DEPLOY RUNBOOK (gated; owner runs): 1) apply migrations 20260225/20260725* in order;
2) deploy edge functions process-withdraw, process-withdrawal, verify-deposit, resolve-dispute;
3) set edge secrets PEOPLE_RPC_ENDPOINT (+ optional ADMIN_WALLETS); 4) ship frontend.
Migrations + functions + frontend must go together or the app breaks.

KNOWN RESIDUAL (Round 2 — as severe as the withdrawal BOLA): release_/lock_/refund_
escrow_internal still have PUBLIC EXECUTE and the client calls release_escrow_internal
directly with the anon key from confirmPaymentReceived -> an anon caller can drain any
victim's LOCKED balance. Fix = a wallet-signed confirm-payment edge function (same
pattern as withdrawals) before revoking PUBLIC execute. Not yet fixed.
2026-07-25 00:09:03 -07:00
pezkuwichain 27b4057bd4 chore(ci/build): typecheck gate, npm ci, backend CI, dep+vuln fixes, hygiene
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).
2026-07-24 23:43:37 -07:00
pezkuwichain 1c6ff3d578 fix(web): correct asset IDs/decimals + chain routing, string-BigInt amounts, presale signer
Fund-logic hardening (audit remediation):
- TransferModal: source asset id/decimals from the canonical MINTABLE_ASSETS
  (wUSDT=1000, wDOT=1001, wETH=1002, wBTC=1003) instead of the wrong hardcoded
  ids (BTC=3 was the deprecated "Old USDT", ETH=4, DOT=5). Route every non-native
  asset through the Asset Hub api; only native HEZ uses the relay balances pallet.
  Removes the latent wrong-asset / wrong-chain transfer.
- Replace BigInt(parseFloat(x)*10**dec) with string-based parseTokenInput in
  TransferModal + 3 DEX init modals; BigInt(float) threw RangeError on common
  fractional amounts (e.g. 100.3 HEZ), breaking sends and pool/bridge init.
- Presale.contribute now resolves a signer via getSigner and passes { signer }
  to signAndSend (was signer-less -> broken for all users).
- presale.ts contribute/refund/claimVested now reject on dropped/invalid/usurped/
  retracted tx states instead of hanging the UI forever.
2026-07-24 22:55:54 -07:00
pezkuwichain 686eecc09e fix(web): repair 6 dead internal links + harden forum markdown against stored XSS
Routing: footer "Explorer"/"Validators" pointed to non-existent /network (4 spots
in AppLayout + LandingPageDesktop) and Explorer/Forum quick-links pointed to bare
/governance (no route) -> all hit the 404 catch-all. Repointed to /explorer and
/governance/assembly; footer "Vote" /-> /elections.

Security (stored XSS): DiscussionThread.parseMarkdown did regex->HTML then
dangerouslySetInnerHTML with no escaping/sanitizer, so <img onerror>/javascript:
links in user comments executed. Now escape HTML first, then DOMPurify-sanitize the
output to a safe tag/attr allow-list with http(s)-only hrefs.
2026-07-24 22:43:30 -07:00
pezkuwichain 0651b80eb7 fix: sync renewal topup with the live bridge config (10K -> 200K wUSDT)
STANDARD_RENEWAL_TOPUP still had the bot's old built-in default (10,000).
The live pezbridge_bot_config.json and the Android wallet's
BridgeMultisigConstants both renew to 200,000 (threshold 40,000) - and all
three signing channels must build the same call, or the hash differs and
real pending renewals stop auto-matching in the /multisig/pending UI.
2026-07-19 01:48:13 -07:00
pezkuwichain dd58fe9164 Add multisig pending-operations UI, fix broken multisig address calc, new signing portal
- calculateMultisigAddress was completely broken (hex-decoded an SS58 string
  and never hashed the preimage) - fixed via @pezkuwi/util-crypto's real
  encodeMultiAddress/createKeyMulti, verified against the actual known
  multisig address on-chain.
- ReservesDashboardPage had stale Noter/Berdevk addresses that don't match
  the real signers - centralized as BRIDGE_MULTISIG_SPECIFIC_ADDRESSES.
- USDTBridge withdrawal called assets.burn directly as a single-signer
  extrinsic (always fails - only the multisig is Admin) while only
  checking status.isFinalized (a failed dispatch is still finalized, so it
  silently did nothing) - replaced with the correct transfer-to-custody
  flow the relayer actually watches for.
- New MultisigOperationsPage (/multisig/pending) lists pending calls from
  real Multisig.Multisigs storage and lets any of the 5 signers
  approve/reject with their own wallet extension.
- New standalone sign/ app (deployed separately at
  pezbridge-sign.pex.mom) - a dedicated, gated signing portal for the same
  operations, so signing isn't dependent on this app alone.
2026-07-14 07:19:34 -07:00
pezkuwichain fe3c65a706 governance: redesign Assembly into on-chain parliament landing (6 languages)
Replace the placeholder Assembly page with a professional on-chain governance
landing (animated 201-seat hemicycle, live chain ticker, blocs, sessions, active
votes, features). Content is fully localized into the 6 standard app languages
(en/tr/kmr/ckb/fa/ar) with RTL for ckb/fa/ar; each language variant is lazy-loaded.
2026-06-30 23:27:19 -07:00
pezkuwichain ed07878646 fix(auth): make password reset work via Supabase native recovery flow (#19)
PasswordReset called a non-existent 'password-reset' edge function, so
users could never reset a forgotten password. Switch to Supabase Auth's
built-in recovery: resetPasswordForEmail() to request the email, and
updateUser({password}) within the PASSWORD_RECOVERY session to set the
new password. Generic success message (no account enumeration); sign out
after reset to force clean re-login.
2026-06-24 16:58:12 -07:00
pezkuwichain 7dddae633a identity: remove unused issueDate const (lint fix) 2026-06-20 18:28:38 -07:00
pezkuwichain a92d61db8a identity: redesign e-ID + passport to match the Digital Kurdistan brand
Rebuild the /identity ID card and passport as flip cards rendered fully in
code (HTML/CSS/SVG) with each citizen's real data:

- e-ID: holographic light card, front (BÊ KURDISTAN JÎYANE NÎNE, photo, gold
  chip, bilingual fields NAV/NAME · PASNAV/SURNAME · DATE OF BIRTH ·
  NATIONALITY KURDISTANÎ/KURDISH · ID NUMBER, faux-QR, e-ID: KOMARA KURDISTAN
  footer) + back (DIGITAL KURDISTAN STATE: ID number, digital wallet ID,
  biometric status, valid-until, gov services, support; authority seal).
- Passport: navy cover with the gold ram (mouflon) emblem + Sorani/English
  titles, flips to a holographic data page (flag stripe, bilingual fields,
  photo, ICAO-style MRZ).
- Adds a Surname field and pulls the connected wallet address for the e-ID
  back. Document labels are the official bilingual set (not UI-translated);
  data stays device-local. Tap ↻ to flip.
2026-06-20 17:53:56 -07:00
pezkuwichain 6e55418703 assets: add pezkuwi-global-union image (for upcoming Global Union surface) 2026-06-20 06:35:35 -07:00
pezkuwichain 56b442fdff chore(web): drop dead bundled docs — app uses external docs.pezkuwichain.io
The in-app /docs route (web/src/pages/Docs.tsx, since c56e021a, Apr 2026) only
redirects to https://docs.pezkuwichain.io; it no longer reads the local
docs-structure.json or web/public/docs/*.md. The 907 bundled markdown + rustdoc
files under web/public/docs and web/public/sdk_docs are generated from
Pezkuwi-SDK by generate-docs-structure.cjs and are not served at runtime, so
they are removed from tracking and gitignored to stop the recurring tree churn.
2026-06-20 06:05:51 -07:00
pezkuwichain 35d7ab38fa i18n: translate the P2P/Buy-Sell nav label per language
The Finance 'P2P/Buy-Sell' tile label was the hardcoded Latin string
'P2P/Buy-Sell' in all six locales, so it stayed untranslated (English) when
switching to Sorani/Farsi/Arabic/Turkish/Kurmancî. Keep the universal 'P2P'
token and translate the buy-sell part (TR Al-Sat, KMR Kirîn-Firotin,
CKB کڕین-فرۆشتن, FA خرید-فروش, AR بيع-شراء).
2026-06-20 05:57:19 -07:00
pezkuwichain 070d682759 feat(p2p): rename Finance tile to P2P/Buy-Sell + add 'Buy with Visa' nav action
The Finance section tile label becomes 'P2P/Buy-Sell'. In the P2P dashboard's
top nav, next to the Messages icon, a Visa card (CreditCard) action 'Buy with
Visa' opens buy-sell.pezkiwi.app in a new tab. Adds p2pNav.buyVisa in all 6
locales.
2026-06-19 17:37:14 -07:00
pezkuwichain cd56ab8fb6 feat(social): replace Help tile with Loto (Newroz) → loto.pex.mom
In the Social section of both the desktop app grid and the mobile home, the Help
tile becomes a Loto tile with the Newroz flame logo (loto-icon.svg) that opens
loto.pex.mom in a new tab. Adds the mobile.app.loto i18n key in all 6 locales.
Help remains reachable from the landing page.
2026-06-19 17:32:57 -07:00
pezkuwichain b012fcaaac fix(security): patch ws (high DoS) and dompurify (XSS) via npm audit fix
Unblocks the deploy security gate — production deps only, no major bumps.
2026-06-15 18:07:37 -07:00
pezkuwichain 7a1d3e7917 feat(social): wire DKS Rojname → news.pex.mom and Events → kurdishtts.pezkiwi.app
- KurdMedia: DKS Rojname channel now links to the Dijital Kurdistan News site
- Social Events item opens the Kurdish TTS app (was coming-soon/locked)
- applies across mobile drawer, mobile home and desktop landing pallets
2026-06-15 17:54:19 -07:00
pezkuwichain 2ee3caac0d fix(ci): audit only production deps in the deploy gate (--omit=dev) (#18)
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.
2026-06-12 23:39:55 -07:00
pezkuwichain 78e93e9766 feat(web): PEZ-20 badge on PEZ & USDT balance cards (#17)
* fix(ci): unblock deploy pipeline (audit gate + orphan submodule)

The Quality Gate & Deploy pipeline was failing at security-audit
(npm audit --audit-level=high), which blocks telegram-gate and the
whole deploy chain — that is why production was serving a stale bundle.

- npm audit fix (no --force, lockfile only): clears the critical vitest
  advisory (GHSA-5xrq-8626-4rwp) and the high elliptic one; only low-
  severity items remain, so 'npm audit --audit-level=high' now exits 0.
- Remove the orphaned 'exchange' gitlink: it is an empty submodule
  pointer with no .gitmodules mapping, which made git print
  'fatal: no submodule mapping found' during checkout.

Verified: lint, test (32 passed), and vite build all pass; audit gate
is green. No package.json changes.

* feat(web): PEZ-20 badge on PEZ and USDT balance cards

Add a small reusable Pez20Badge pill next to the PEZ and USDT tokens in
the wallet balance view, linking to the Token Standards docs. These are
fungible assets on Asset Hub, i.e. the PEZ-20 standard — this gives users
the familiar ERC-20-style mental model at a glance.

Additive only: no labels removed, native HEZ is intentionally not badged
(it is the native/gas token, not a PEZ-20 asset).
2026-06-12 23:28:05 -07:00
pezkuwichain 83d66feacc fix(ci): unblock deploy pipeline (audit gate + orphan submodule) (#16)
The Quality Gate & Deploy pipeline was failing at security-audit
(npm audit --audit-level=high), which blocks telegram-gate and the
whole deploy chain — that is why production was serving a stale bundle.

- npm audit fix (no --force, lockfile only): clears the critical vitest
  advisory (GHSA-5xrq-8626-4rwp) and the high elliptic one; only low-
  severity items remain, so 'npm audit --audit-level=high' now exits 0.
- Remove the orphaned 'exchange' gitlink: it is an empty submodule
  pointer with no .gitmodules mapping, which made git print
  'fatal: no submodule mapping found' during checkout.

Verified: lint, test (32 passed), and vite build all pass; audit gate
is green. No package.json changes.
2026-06-11 18:42:45 -07:00
pezkuwichain d6ace14e70 fix(web): live collator/nominator counts after AHM + reliable B2B redirect (#15)
Staking migrated to Asset Hub (AHM), but the landing page still read
nominators from the relay (api.query.staking.counterForNominators),
which is now empty there — so the count showed '—'. Collators were read
from collatorSelection.candidates (empty; collators are invulnerables)
and only on Asset Hub, missing the People chain set.

- Nominators: query Asset Hub staking.counterForNominators (verified 30).
- Collators: count collatorSelection.invulnerables on both Asset Hub and
  People chain (2 + 2), tracked per-chain and summed.
- NetworkStats.tsx already used the correct sources; this aligns the
  landing page with it.

B2B button (/bereketli SSO interstitial): if there is no Supabase session
or the token exchange fails, redirect to https://bereketli.pezkiwi.app
instead of stranding the user on app.pezkuwichain.io/bereketli. (The
backend CORS allowlist was also missing app.pezkuwichain.io; fixed
server-side so the SSO exchange itself now succeeds.)
2026-06-11 16:41:14 -07:00
pezkuwichain 2cbfd21539 fix(cosign): explicit GHCR login before sign + verify
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.
2026-05-09 13:41:29 +03:00
pezkuwichain f7c070e45b fix(deps): drop invalid create-ecdh override (max version is 4.x not 5.x)
The earlier npm override 'create-ecdh: ^5.0.1' resolved to no version on
the registry. CI install failed with ETARGET. Removing the override —
elliptic override alone covers the high-severity transitive vulns.
Remaining 6 lows in vite-plugin-node-polyfills chain accepted.
2026-05-09 12:27:07 +03:00
pezkuwichain 06ed9734c6 ci(security): Faz 3 + ekstra — runner consolidation, auto-rollback, cosign, SRI, dep cleanup
* 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.
2026-05-09 12:08:49 +03:00
pezkuwichain d93d4c6cd0 fix(docker): correct dist path after WORKDIR=/build/web
Stage 2 was looking for /build/dist but vite emits to /build/web/dist
(WORKDIR is /build/web in stage 1). Fix the COPY --from=builder path.
2026-05-08 21:39:07 +03:00