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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Spike 608 (#472, phase-175): Automated dependency-update cadence with
# grouped Soroban (Cargo) and Stellar / Next.js (npm) pull requests.
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "03:00"
open-pull-requests-limit: 5
groups:
stellar-sdk-root:
patterns:
- "@stellar/*"
- "@creit.tech/stellar-wallets-kit"
- "x402-stellar"
nextjs-react-bonded:
patterns:
- "next"
- "react"
- "react-dom"
- "@types/react"
- "@types/react-dom"
vitest-bonded:
patterns:
- "vitest"
- "@vitest/coverage-v8"

- package-ecosystem: "npm"
directory: "/scripts"
schedule:
interval: "weekly"
day: "monday"
time: "03:00"
open-pull-requests-limit: 3
groups:
stellar-sdk-scripts:
patterns:
- "@stellar/*"

- package-ecosystem: "cargo"
directory: "/contracts/phase-protocol"
schedule:
interval: "weekly"
day: "monday"
time: "03:30"
groups:
soroban-sdk-phase-protocol:
patterns:
- "soroban-*"

- package-ecosystem: "cargo"
directory: "/contracts/mock-token"
schedule:
interval: "weekly"
day: "monday"
time: "03:30"
groups:
soroban-sdk-mock-token:
patterns:
- "soroban-*"

- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "04:00"
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ jobs:
cache: npm
- name: Install locked dependencies
run: npm ci
- name: Verify dependency cadence and Soroban/Stellar parity
run: npm run deps:cadence
- name: Lint
# eslint had no config/devDependency before this change (npm run lint
# failed outright); now that it runs, ~95 pre-existing errors surface
Expand Down Expand Up @@ -81,7 +83,7 @@ jobs:
contracts/mock-token
contracts/phase-protocol
- name: Install Stellar CLI
run: cargo install --locked stellar-cli --version ^23
run: cargo install --locked stellar-cli --version 23.0.0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/dependency-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,23 @@ on:
paths:
- "package.json"
- "package-lock.json"
- "scripts/package.json"
- "scripts/package-lock.json"
- "contracts/**/Cargo.toml"
- "contracts/**/Cargo.lock"
- ".github/dependabot.yml"
- ".github/workflows/dependency-audit.yml"
push:
branches:
- main
paths:
- "package.json"
- "package-lock.json"
- "scripts/package.json"
- "scripts/package-lock.json"
- "contracts/**/Cargo.toml"
- "contracts/**/Cargo.lock"
- ".github/dependabot.yml"
- ".github/workflows/dependency-audit.yml"
schedule:
- cron: "17 3 * * 1"
Expand All @@ -31,5 +41,7 @@ jobs:
cache: npm
- name: Install locked dependencies
run: npm ci
- name: Verify grouped Soroban and Stellar dependency parity
run: npm run deps:cadence
- name: Scan dependencies for high severity vulnerabilities
run: npm run security:audit
2 changes: 2 additions & 0 deletions .github/workflows/secret-rotation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ jobs:
cache: npm
- name: Install locked dependencies
run: npm ci
- name: Verify dependency cadence alignment
run: npm run deps:cadence
- name: Check rotation metadata
run: npm run security:secret-rotation
env:
Expand Down
4 changes: 3 additions & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# x402-stellar peers @stellar/stellar-sdk@^12; this app uses ^15. Peers align at runtime; strict install would fail (e.g. Vercel).
# x402-stellar peers @stellar/stellar-sdk@^12; root and scripts/ both align on @stellar/stellar-sdk@^15.0.1 (Spike 608 / phase-175).
legacy-peer-deps=true
engine-strict=false

16 changes: 8 additions & 8 deletions PROJECT_ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,14 +212,14 @@ is a fixed-size digest of the canonical payload, keeping it under wallet
| `phase-167` | `NEXT_PUBLIC_FEATURE_PHASE_167` / `FEATURE_PHASE_167` | WalletConnect v2 mobile session resilience (`lib/walletconnect-resilience.ts`): configurable relay URL, multi-chain support, session persistence, chain-switch guard, and mobile deep-link return. Spike 623 (issue #486) | off | Unset var, restart — WalletConnect banner and reconnect actions disabled, kit falls back to default single-session behavior |
| `phase-168` | `NEXT_PUBLIC_FEATURE_PHASE_168` / `FEATURE_PHASE_168` | xBull wallet full support and parity (`lib/xbull-adapter.ts`): Freighter-difference matrix, xBull module detection, sign result normalization, trustline error translation, and badge surfacing. Spike 624 (issue #487) | off | Unset var, restart — xBull transactions fall through generic kit handlers, badge hidden |
| `phase-169` | `NEXT_PUBLIC_FEATURE_PHASE_169` / `FEATURE_PHASE_169` | Ledger blind-signing to clear-signing review for Soroban (`lib/ledger-soroban-decoder.ts`): invokeHostFunction pre-image decode, verification code, on-device review modal, Gate #13 preemptive validation, and hardware error humanizer. Spike 625 (issue #488) | off | Unset var, restart — Ledger transactions fall back to default blind-signing flow without preflight review modal |
| `phase-170` | `NEXT_PUBLIC_FEATURE_PHASE_170` / `FEATURE_PHASE_170` | Structured log pipeline (`lib/log-pipeline.ts`): deep secret redaction, deterministic per-correlation-id info sampling (warn/error always kept), closed index-label set (`level`, `event`, `route`, `method`, `status_class`), and a bounded non-blocking HTTPS batcher (`LOG_SINK_URL`, https only). `api-observability` routes every line through it. Spike 606 (issue #470) — partial slice: no `waitUntil` flush on freeze, no counter export, ad-hoc `console.*` callers not migrated | off | Unset var, restart — `emitLog` is the identity and console lines are written exactly as before; in-memory buffer dropped |
| `phase-171` | `NEXT_PUBLIC_FEATURE_PHASE_171` / `FEATURE_PHASE_171` | Backup and DR snapshots (`lib/backup-snapshot.ts`): lock-consistent sidecar set + `VACUUM INTO` SQLite image, sha256/row-count manifest, `integrity_check`/`foreign_key_check` verify, tmp+rename restore with a restore marker that makes `/api/cron/faucet-health` return 503, and an AES-256-GCM sealed secrets envelope. `scripts/backup-drill.ts` runs snapshot→verify→restore→parity. Spike 605 (issue #469) — partial slice: no off-box shipping or scheduled snapshots | off | Unset var, restart — entry points throw `FLAG_DISABLED`, restore marker ignored; snapshots on disk are inert |
| `phase-172` | `NEXT_PUBLIC_FEATURE_PHASE_172` / `FEATURE_PHASE_172` | SQLite→Postgres rehearsal (`lib/db-parity.ts`): dialect helpers (`?`→`$n`, type map, explicit upsert), typed per-table parity (value, `type_coercion`, checksum), per-test schema names, and a `DualWriter` (primary first, bounded/timeout-guarded secondary, drift log, `primary-only` rollback). Needs `PHASE_DATABASE_URL` to enter `dual` mode. Spike 604 (issue #468) — partial slice: no Postgres driver, async CAS re-plumbing or pool storm test | off | Unset var, restart — `resolveDualWriteMode` returns `primary-only`; SQLite stays authoritative, nothing in production paths calls the module |
| `phase-173` | `NEXT_PUBLIC_FEATURE_PHASE_173` / `FEATURE_PHASE_173` | Vercel preview isolation (`lib/preview-isolation.ts`): per-preview data root (`<root>/previews/<pr-N or br-slug-hash>`), `validatePhaseEnv` fails a preview that shares the production issuer, cron secret, contract IDs, or runs mainnet (declared via `PHASE_PROD_ISSUER`, `PHASE_PROD_CRON_SECRET_SHA256`, `PHASE_PROD_CONTRACT_IDS`), preview cron skipped unless `PHASE_PREVIEW_CRON_ENABLED=1`, and `x-vercel-cron` no longer trusted in previews. Spike 603 (issue #467) — partial slice: no per-PR contract deploy, preview CI job, or branch-DB seeding | off | Unset var, restart — data root, cron auth and env validation revert; per-preview directories stay inert |
| `phase-178` | `NEXT_PUBLIC_FEATURE_PHASE_178` / `FEATURE_PHASE_178` | Leaderboard integrity (`lib/leaderboard-integrity.ts`): multiplicative scoring `decayed × farmPenalty × sybilDiscount` — exponential half-life decay (7d), anti-farm rules (`event_burst`, `counterparty_concentration`, `point_velocity`) floored at 0.15, and sybil band discounts (trusted 1 / caution 0.7 / suspect 0.35, missing band = caution). `GET /api/leaderboard` ranks; admin `POST /api/leaderboard` scores before persisting to `leaderboard-entries.json`. Spike 616 (issue #479) — partial slice: no on-chain sybil fetch in-route; callers supply the band | off | Unset var, restart — routes 404, `leaderboard-entries.json` stays inert |
| `phase-179` | `NEXT_PUBLIC_FEATURE_PHASE_179` / `FEATURE_PHASE_179` | Retention and GDPR erasure (`lib/retention-erasure.ts`): TTL sweeps (expired SEP-10 sessions, consumed challenges, 90-day notifications — fail-safe on missing timestamps) via `GET /api/cron/retention-sweep` (daily 03:15, preview + restore fenced); `POST /api/user/erasure` deletes wallet-owned records across five sidecar domains behind `assertNoLegalHold` (423 on hold) and appends fingerprint-only receipts to `gdpr-erasure-log.json`. Spike 617 (issue #480) — partial slice: SQLite tables not walked; consent ledger deliberately retained per Art. 17(3)(b) | off | Unset var, restart — routes 404, receipts stay inert; nothing is deleted while off |
| `phase-180` | `NEXT_PUBLIC_FEATURE_PHASE_180` / `FEATURE_PHASE_180` | Analytics warehouse export (`lib/analytics-warehouse.ts`): flat row model (`counter`/`health`/`volume` tables) with an unconditional redaction pass (wallets fingerprinted, secrets stripped), schema-versioned manifest (`phase-warehouse-export` 1.0.0, rows-only sha256 checksum) emitted as NDJSON header line or Parquet `key_value_metadata` (`hyparquet-writer`). Admin-keyed `GET /api/analytics/export?format=ndjson\|parquet` streams downloads with `X-Warehouse-Schema-Version`/`X-Warehouse-Checksum`. Spike 618 (issue #481) — partial slice: three source tables only | off | Unset var, restart — route 404; no stored warehouse state, collectors read live sources |
| `phase-181` | `NEXT_PUBLIC_FEATURE_PHASE_181` / `FEATURE_PHASE_181` | Anomaly detection (`lib/anomaly-detection.ts`): robust z-score (median+MAD, σ floor) on wash-excluded volume and per-run counter deltas (own counters excluded), deterministic balance drop tiers (≥15% warning, ≥35% critical), detection-before-push baselines in `anomaly-baselines.json` (capped ring). `GET /api/cron/anomaly-detect` (hourly, minute 5, preview + restore fenced) routes findings to webhooks with distinct kind+metric titles. Spike 619 (issue #482) — partial slice: no per-channel thresholds or suppressions | off | Unset var, restart — cron 404s, baselines stay inert, no alerts routed |
| `phase-170` | `NEXT_PUBLIC_FEATURE_PHASE_170` / `FEATURE_PHASE_170` | Albedo full retirement-or-parity plan with flagged sunset (`lib/albedo-sunset.ts`): call-site migration registry, parity-freeze (no new integrations, pinned `@albedo-link/intent` 0.12.0) and retired modes (`ALBEDO_SUNSET_MODE`), fail-closed Albedo signing with migration copy, and sunset banner UI. Spike 626 (issue #489) | off | Unset var, restart — sunset banner hidden, `/api/albedo/status` returns 404, every Albedo call site reverts to legacy guarded behavior |
| `phase-171` | `NEXT_PUBLIC_FEATURE_PHASE_171` / `FEATURE_PHASE_171` | Freighter mobile browser support (`lib/freighter-mobile.ts`): mobile/in-app UA detection, injection-based support matrix, viewport-fit + theme-color adaptation, visualViewport keyboard avoidance, same-tab friendbot strategy, and a sessionStorage connect/sign return-flow marker. Spike 627 (issue #490) | off | Unset var, restart — mobile notice renders nothing, viewport/keyboard adaptation skipped, friendbot reverts to legacy `window.open` behavior |
| `phase-172` | `NEXT_PUBLIC_FEATURE_PHASE_172` / `FEATURE_PHASE_172` | Biometric unlock for returning wallets with OS-gated session (`lib/biometric-unlock.ts`): WebAuthn platform-authenticator gate over an AES-GCM sealed session envelope (wallet-bound challenge, non-extractable HKDF key), attempt lockout with cooldown, and lock-screen tap-to-unlock middle state in `WalletProvider`. Spike 628 (issue #491) | off | Unset var, restart — lock screen never mounts, auto-restore behaves as before; `biometric-session-envelope` stays inert ciphertext |
| `phase-173` | `NEXT_PUBLIC_FEATURE_PHASE_173` / `FEATURE_PHASE_173` | Horizon-backed per-wallet transaction history timeline (`lib/wallet-history.ts`): `GET /api/history` serving merged `transactions?account=` + `effects?account=` reads through the Horizon bulkhead, effects classification, settle ↔ forge invoice correlation (settlement idempotency + generation-job stores), and an in-memory per-wallet index-cache with stale-on-error degrade wired to the dashboard timeline. Spike 629 (issue #492) | off | Unset var, restart — `/api/history` returns 404, dashboard timeline renders nothing, cache never read/written (no sidecars) |
| `phase-174` | `NEXT_PUBLIC_FEATURE_PHASE_174` / `FEATURE_PHASE_174` | Feature-flag ops: staged rollout, per-wallet targeting and kill-switch drills (`lib/feature-flags.ts`, `app/api/admin/feature-flags/route.ts`): CAS-versioned runtime rules (`feature-flag-ops.json`), emergency kill-switch overriding build-time `NEXT_PUBLIC_*` on server write paths, FNV-1a basis-point rollout ramps (`0..10_000`), stale-on-error rule caching, and non-destructive kill-switch drills proving distributor auto-refill halt and `faucetPending` mid-poll idempotency. Spike 607 (issue #471) | off | Unset var, restart — `/api/admin/feature-flags` returns 404, flags revert to static env evaluation, `feature-flag-ops.json` stays inert |
| `phase-175` | `NEXT_PUBLIC_FEATURE_PHASE_175` / `FEATURE_PHASE_175` | Dependency-update cadence automation with grouped Soroban and Stellar updates (`lib/dependency-cadence.ts`, `.github/dependabot.yml`, `scripts/check-dependency-cadence.ts`): unifies `@stellar/stellar-sdk` `^15.0.1` across root and `scripts/`, aligns `scripts/package.json` `build:phase`/`optimize:phase` on `wasm32-unknown-unknown`, pins `stellar-cli` `23.0.0` and `packageManager: "npm@10.9.2"`, exposes `soroban_abi_version() -> 22000` on `phase-protocol` and `mock-token`, and gates WASM bytecode hash changes on staging dry-run verification. Spike 608 (issue #472) | off | Unset var, restart — `/api/faucet` omits `sorobanAbiVersion` metadata, runtime cadence checks stay inert |
| `phase-176` | `NEXT_PUBLIC_FEATURE_PHASE_176` / `FEATURE_PHASE_176` | Unified analytics event warehouse across faucet, claim, notifications, forge, and push funnels (`lib/analytics-warehouse.ts`): SQLite WAL persistence with unique deduplication key idempotency, session stitching (`session_id` -> `wallet`), bounded closed-label metadata sanitization, and backward-compatible migration from faucet 5k-ring sidecar. Spike 610 (issue #473) | off | Unset var, restart — event warehouse write paths halt safely; existing `analytics_events` rows remain on disk; routes fall back to in-memory/legacy sidecar reads |
| `phase-177` | `NEXT_PUBLIC_FEATURE_PHASE_177` / `FEATURE_PHASE_177` | Funnel analytics v2 with retention cohorts: Day-N (Day-1, Day-3, Day-7, Day-14, Day-30), time-to-convert tracking (mean, p50, p90), wallet-age bands (`<24h`, `1-7d`, `7-30d`, `>30d`) with in-memory Horizon bulkhead cache, quest cohorts, and sybil-weighted retention joins. Spike 611 (issue #474) | off | Unset var, restart — `/api/notifications?retention=1` returns 404, analytics revert to v1 `computeClaimFunnel`; referral store locking retains race-safety |



Expand Down
Loading