Policy-Controlled Payment Firewall for Autonomous Agent Actions on Stellar
Fortexa is a policy-controlled payment firewall for autonomous agent actions on Stellar. It sits between agent intent and economic execution, applies governance/risk checks, and keeps an auditable decision trail.
This document reflects the current implementation in this repository.
Agentic systems can now trigger real payments. That creates a new risk layer: high-speed model decisions can become high-impact economic actions.
Fortexa adds a control plane between intent and money movement:
- Policy checks before execution
- Risk scoring on suspicious behavior
- Human-approval gate for sensitive cases
- Wallet-native signed XDR flow
- Auditable evidence trail for every decision
In short: Fortexa is the safety layer for agentic payments.
If you only read one section, read this:
- Login with wallet on
/login. - Evaluate action in
/console. - Receive decision:
BLOCK/REQUIRE_APPROVAL/WARN/APPROVE. - For allowed flows, build unsigned XDR β sign in wallet β submit signed XDR.
- Verify outcome with Explorer link and inspect evidence in
/activityand/ops.
Fortexa currently runs with a strict wallet-bound model:
- User logs in with wallet (
/login). - Session is created with role (
operator/viewer). - Session wallet is bound as execution source.
- Actions are evaluated by policy + security engine.
- Approved/warned decisions can proceed to signed-XDR payment flow.
- Decision/audit evidence is stored and visible in
/activityand/ops.
- Login payload: wallet public key (
G...). - Role is resolved via allowlists:
FORTEXA_OPERATOR_WALLETSFORTEXA_VIEWER_WALLETS
- If both allowlists are empty, current behavior falls back to
operatorrole for any valid wallet (recommended only for local/dev). - Session cookie:
fortexa_session(HMAC-signed).
operator: full decision/policy/payment flowviewer: read-only experience on sensitive execution paths
- Rate limiting
- Brute-force lockout (
FORTEXA_AUTH_MAX_ATTEMPTS,FORTEXA_AUTH_LOCK_MINUTES)
Note: MFA is removed from current implementation.
Fortexa currently does not perform server-side signing or private-key custody.
- Session is wallet-bound at login.
- Execution source wallet is derived from session identity.
- Manual arbitrary wallet assignment in UI is removed.
/api/stellar/balanceauto-syncs missing wallet mapping from session when possible.
- Policy engine:
src/lib/policy/engine.ts - Security analyzer:
src/lib/security/analyzer.ts - Decision engine:
src/lib/decision/engine.ts
Decision outcomes:
BLOCKREQUIRE_APPROVALWARNAPPROVE
Human Approve & Re-run applies only when prior result is REQUIRE_APPROVAL.
- Evaluate action in
/console. - Build unsigned tx:
POST /api/stellar/build-payment. Submit Signed XDRorchestrates signing/submission path:- if signed input is already present β submit directly
- if unsigned input is present β wallet signing is triggered first, then submit
- Submit signed tx:
POST /api/stellar/submit-signed. - Explorer URL is returned and shown as clickable link.
Additional behavior:
- XDR build timeout configured to 180 seconds.
- Submit errors include Horizon result codes when available.
- Decisions are appended to audit store at evaluation time.
/activityreads entries by authenticated session user id.- Export endpoint supports
mineandallscopes in JSON/CSV.
- Node.js 20+
- npm 10+
npm install
cp .env.example .env.local
npm run devOpen: http://localhost:3000
Reference (.env.example):
STELLAR_HORIZON_URL=https://horizon-testnet.stellar.org
DATABASE_URL=
DATABASE_SSL=false
FORTEXA_STORE_DIR=
FORTEXA_SHARED_STATE_PATH=
GROQ_API_KEY=
GROQ_MODEL=llama-3.3-70b-versatile
FORTEXA_AUTH_SECRET=
FORTEXA_OPERATOR_WALLETS=
FORTEXA_VIEWER_WALLETS=
FORTEXA_AUTH_MAX_ATTEMPTS=5
FORTEXA_AUTH_LOCK_MINUTES=10
NEXT_PUBLIC_STELLAR_DESTINATION=npm run dev
npm run build
npm run start
npm run lint
npm run test
npm run test:watch
npm run demo:scenarios
npm run db:migratePOST /api/auth/loginPOST /api/auth/logoutGET /api/auth/sessionPOST /api/auth/refresh
GET /api/policyPOST /api/policy(operator)GET /api/policy/history(operator)POST /api/policy/rollback(operator)
POST /api/decision(operator)POST /api/agent/plan(operator, Groq-backed)
GET /api/auditGET /api/audit/export?format=json|csv&scope=mine|allGET /api/healthGET /api/metrics(?format=prometheus)
GET /api/stellar/balancePOST /api/stellar/setup(session-wallet bootstrap/sync helper; not manual wallet linking)POST /api/stellar/build-paymentPOST /api/stellar/submit-signedPOST /api/stellar/pay(legacy disabled)POST /api/stellar/fund(removed behavior, returns410)
/β Overview dashboard/loginβ Wallet-only authentication (Connect Wallet)/walletβ Session wallet status and balance/consoleβ Decisioning + payment execution console/policiesβ Policy editor, history, rollback/scenariosβ Scenario gallery/activityβ Audit trail timeline/opsβ Operations/telemetry dashboard
- Health endpoint:
GET /api/health - Metrics endpoint:
GET /api/metrics+ Prometheus format /opsdashboard shows:- service health
- total requests
- error rate
- signed tx count
- top routes + rolling trend
Ops dashboard initial load is optimized so core telemetry renders first; slow TX-count fetch no longer blocks first paint.
Stores include:
audit-storepolicy-storeuser-wallet-store
If DATABASE_URL is available and healthy, Postgres is used.
Otherwise Fortexa falls back to local JSON files:
- local/dev default:
.fortexa/*.json - Vercel default:
/tmp/fortexa/*.json
Optional overrides:
FORTEXA_STORE_DIRto set file-store directory explicitlyFORTEXA_SHARED_STATE_PATHfor shared lockout/rate-limit state file path- use an absolute path on Vercel (example:
/tmp/fortexa/shared-security-state.json)
- use an absolute path on Vercel (example:
- Migrations:
src/lib/storage/migrations.ts - Runner:
src/lib/storage/db.ts - Tracking table:
fortexa_schema_migrations - Manual run:
npm run db:migrate
- Framework: Next.js App Router (
next@16) - Language: TypeScript
- UI: Tailwind CSS + custom UI primitives
- Validation:
zod - Charts:
recharts - Stellar:
@stellar/stellar-sdk, optional@stellar/freighter-api - Database:
pg(optional Postgres, file fallback enabled) - Tests: Vitest
- Shared security state is file-based (not distributed locking like Redis).
- Risk scoring remains heuristic-heavy (no external threat-intel integration).
- Stellar workflow is testnet-oriented.
- Server-side signing remains intentionally disabled.
- Full end-to-end automated coverage for the complete decision-to-payment lifecycle is still limited.
Fortexa is intentionally optimized for hackathon clarity and wallet-native control, not full production deployment.
- Introduce distributed shared state backend (Redis).
- Add stronger risk intelligence + anomaly detection.
- Expand end-to-end payment verification and automated lifecycle tests.
MIT (see package.json).
