Skip to content

About

SafeTrust is a decentralized P2P escrow platform built on Stellar securing deposits for hotels, vacation rentals, and tourism bookings without intermediaries.

Resources

Stars

0 stars

Watchers

0 watching

Forks

ย 
ย 

Latest commit

ย 

History

798 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

SafeTrust Logo

๐Ÿ›ก๏ธ backend-SafeTrust

โšก Hasura GraphQL ยท ๐Ÿ˜ PostgreSQL ยท ๐Ÿช Webhook Service ยท ๐Ÿข Multi-tenant

License: MIT Hasura Stellar ๐Ÿ” TrustlessWork ๐Ÿ’ง Drips Wave ๐ŸฆŠ GrantFox


๐Ÿ“– Table of Contents


๐Ÿ” What is this repo?

The backend infrastructure for SafeTrust โ€” a decentralized P2P escrow platform for rental transactions on the Stellar blockchain. This repo contains:

  • โšก Hasura GraphQL Engine โ€” auto-generated API with JWT auth and row-level permissions
  • ๐Ÿ˜ PostgreSQL โ€” multi-tenant schema (safetrust + hotel_industry)
  • ๐Ÿช Webhook service โ€” Node/Express, handles Firebase auth sync and escrow lifecycle events
  • ๐Ÿ“‚ Migrations + seeds โ€” versioned schema and dev data for both tenants

๐Ÿš€ Quick Start

Prerequisites

Tool Version
Docker + Docker Compose โ‰ฅ 24
Hasura CLI โ‰ฅ 2.x
curl any

๐Ÿ’ก Windows Note: Run bin/start inside WSL (Ubuntu) or Git Bash.

1. Set up environment variables

cp .env.example .env

Fill in .env before running anything:

POSTGRES_PASSWORD=your_postgres_password

# Must be valid JSON, key โ‰ฅ 32 characters for HS256
HASURA_GRAPHQL_JWT_SECRET={"type":"HS256","key":"replace-with-min-32-char-secret-here"}

HASURA_EVENT_SECRET=your_event_secret

โš ๏ธ HASURA_GRAPHQL_JWT_SECRET must be valid JSON with a key of at least 32 characters. start will fail if this is malformed.

2. ๐Ÿ”ฎ Start everything

bin/start tenant-name tenant-name

start runs in order:

Step Action
1 Start postgres, graphql-engine, webhook containers
2 Poll GET /healthz until Hasura is ready (up to 3 min)
3 Build and deploy tenant metadata for all tenants
4 Apply all migrations per tenant
5 Reload Hasura metadata
6 Apply seed data per tenant in parallel, with one transaction per seed file

Seed files are applied directly with PostgreSQL and ON_ERROR_STOP=1. Each file is wrapped in BEGIN/COMMIT, so a failed file rolls back completely; the two independent tenants run concurrently. Seed inserts use conflict-safe or stable-key cleanup patterns, making bin/start safe to rerun without docker compose down -v.

Set SEED_CHUNK_SIZE in .env to tune the configured seed batch size (default 500).

Target a specific tenant:

bin/start safetrust                 # one tenant
bin/start safetrust hotel_industry # both explicitly

Reset the database

bin/start --reset safetrust

--reset removes the Docker volumes before starting, so tenant data is recreated by migrations and seeds. To recreate the containers while keeping the database volumes, use:

bin/start --restart safetrust

Rollback migrations

# Rollback safetrust migrations:
hasura migrate apply \
  --endpoint http://localhost:8080 \
  --admin-secret myadminsecretkey \
  --database-name safetrust \
  --down all

# Rollback hotel_industry migrations:
hasura migrate apply \
  --endpoint http://localhost:8080 \
  --admin-secret myadminsecretkey \
  --database-name hotel_industry \
  --down all

๐Ÿ—๏ธ Metadata Architecture

metadata/
โ”œโ”€โ”€ base/                โ† shared Hasura config across all tenants
โ”œโ”€โ”€ tenants/
โ”‚   โ”œโ”€โ”€ safetrust/       โ† apartments, escrows, users, wallets
โ”‚   โ””โ”€โ”€ hotel_industry/  โ† hotels, rooms, reservations, escrow_transactions
โ”œโ”€โ”€ build/               โ† generated output (tenants merged with base), ready to deploy
โ”œโ”€โ”€ build-metadata.sh
โ”œโ”€โ”€ deploy-tenant.sh
โ””โ”€โ”€ setup-tenant.sh      โ† runs build + deploy in one command โœ…

SafeTrust user profile permissions

The host role can read only its own user's display_name and avatar_url, scoped by id = x-hasura-user-id. Host access does not include the user's email, ID, host flag, or timestamps. No cross-user profile relationship is currently authorized; add a separately scoped permission only when its business rule is defined. This permission supports the host self-profile workflow: reading back the display name and avatar that the host can update on their own account. Those presentation fields are the minimum needed for that workflow; it does not require access to contact details, account identifiers, timestamps, or another user's profile. This follows the minimum profile-access policy in issue #668. The guest role's self-only row filter is unchanged.

In Hasura, filter: {} means all rows are permitted for that role; it does not mean no rows. The checked-in public_users.yaml currently has a self-only host filter, not an empty one. Its row restriction also applies to list, primary-key, relationship, and aggregate query paths; host aggregation is not enabled.

The checked-in .env.example configures HASURA_GRAPHQL_JWT_SECRET with the Firebase issuer and Hasura claims namespace. claims.ts constructs the user ID and role claims (x-hasura-user-id, x-hasura-default-role, and x-hasura-allowed-roles). These files document repository configuration and claim construction, but do not verify that this configuration is deployed or that syncClaims has run for issued tokens. A role name in metadata alone does not prove that ordinary clients cannot claim host: deployment must verify that trusted token issuance sets X-Hasura-User-Id to the authenticated user's ID and only grants host in the allowed-role claims to authorized users. Do not use an admin-secret query to validate row permissions; test with two actual authenticated host/guest users and their supported claims.

Local metadata is not evidence of deployed state. Confirm the exported target metadata after the HAS-02 metadata-deployment fix, then test the deployed permissions. This repository does not establish whether the prior broader host field list was ever deployed or reachable, so no deployed exposure or breach is confirmed.


๐Ÿ› ๏ธ Manual Commands (optional)

bin/start handles all of these automatically. Use these only when targeting a specific step in isolation.

Deploy metadata โ€” single tenant

cd metadata
./setup-tenant.sh safetrust --endpoint http://localhost:8080 --admin-secret myadminsecretkey

Migrations โ€” single tenant

hasura migrate apply \
  --database-name safetrust \
  --endpoint http://localhost:8080 \
  --admin-secret myadminsecretkey

Apply a single version

hasura migrate apply \
  --database-name safetrust \
  --version <timestamp> \
  --type up \
  --endpoint http://localhost:8080 \
  --admin-secret myadminsecretkey

Seeds โ€” single tenant

hasura seed apply \
  --database-name safetrust \
  --endpoint http://localhost:8080 \
  --admin-secret myadminsecretkey

For the same transactional behavior as startup, use bin/start for seed application rather than invoking hasura seed apply directly.


๐Ÿ“š Documentation

In-depth guides live in docs/.

Document What it covers
SafeTrust Schema Migration Strategy Why SafeTrust tables moved from public to the safetrust schema, plus the deployment and rollback sequences

๐Ÿฅ‹๐Ÿ”ฌ Karate Tests

API tests using the Karate framework, running in Docker.

bin/test # start karate testing

Reports are generated at:

  • tests/results/karate-summary.html
  • tests/results/karate-tags.html

Add new tests: Create .feature files in tests/karate/features/ โ€” picked up automatically.

Config files:

  • tests/karate/src/test/resources/karate-config.ts (compiled to the JavaScript Karate loads)
  • docker-compose-test.yml
  • Dockerfile.test

๐Ÿ“bin/test uses an isolated safetrust-test Compose project and excludes the @timing benchmark, which requires a separate bin/start safetrust timing report. It runs docker compose -p safetrust-test -f docker-compose-test.yml run --rm --build karate internally.

TypeScript sources and builds

Webhook runtime code and tests live in webhook/src/**/*.ts. Production builds exclude test files; type-check checks both runtime and test sources in strict mode.

npm --prefix webhook ci
npm --prefix webhook run type-check
npm --prefix webhook test
npm --prefix webhook run build

Native-addon helpers, fallback loaders, benchmarks, and examples use TypeScript sources under crates/. Run npm --prefix webhook run build:tooling before invoking their JavaScript entry points directly. build:rust and test do this automatically. The adjacent .js files are generated compatibility outputs; edit the .ts sources.

Karate configuration and token helpers are also authored in TypeScript. The test image compiles them automatically. For a local Java runner, first run npm --prefix webhook run build:karate to generate Karate's required .js files. Karate expects a function expression, so its compiler configuration omits a leading strict-mode directive while retaining strict TypeScript checks. Runtime payload validation and native-verifier fail-closed checks remain necessary.

๐Ÿค Contributing

  1. bin/start must complete without errors.
  2. No raw SQL outside of migrations/ โ€” all schema changes go through versioned migration files.
  3. Never edit a migration that has already been applied โ€” add a new one instead.
  4. Link the issue your PR closes.

Branch naming: feat/<issue-number>-short-description ยท fix/<issue-number>-short-description


๐Ÿ“„ License

ยฉ 2026 SafeTrust. Released under the MIT License.

About

SafeTrust is a decentralized P2P escrow platform built on Stellar securing deposits for hotels, vacation rentals, and tourism bookings without intermediaries.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages