- ๐ What is this repo?
- ๐ Quick Start
- ๐๏ธ Metadata Architecture
- ๐ Documentation
- ๐ ๏ธ Manual Commands (optional)
- ๐ฅ๐ฌ Karate Tests
- ๐ค Contributing
- ๐ License
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
| Tool | Version |
|---|---|
| Docker + Docker Compose | โฅ 24 |
| Hasura CLI | โฅ 2.x |
| curl | any |
๐ก Windows Note: Run
bin/startinside WSL (Ubuntu) or Git Bash.
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_SECRETmust be valid JSON with a key of at least 32 characters.startwill fail if this is malformed.
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
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 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/
โโโ 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 โ
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.
bin/starthandles all of these automatically. Use these only when targeting a specific step in isolation.
cd metadata
./setup-tenant.sh safetrust --endpoint http://localhost:8080 --admin-secret myadminsecretkey
hasura migrate apply \
--database-name safetrust \
--endpoint http://localhost:8080 \
--admin-secret myadminsecretkey
hasura migrate apply \
--database-name safetrust \
--version <timestamp> \
--type up \
--endpoint http://localhost:8080 \
--admin-secret myadminsecretkey
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.
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 |
API tests using the Karate framework, running in Docker.
bin/test # start karate testingReports are generated at:
tests/results/karate-summary.htmltests/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.ymlDockerfile.test
๐
bin/testuses an isolatedsafetrust-testCompose project and excludes the@timingbenchmark, which requires a separatebin/start safetrusttiming report. It runsdocker compose -p safetrust-test -f docker-compose-test.yml run --rm --build karateinternally.
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 buildNative-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.
bin/startmust complete without errors.- No raw SQL outside of
migrations/โ all schema changes go through versioned migration files. - Never edit a migration that has already been applied โ add a new one instead.
- Link the issue your PR closes.
Branch naming: feat/<issue-number>-short-description ยท fix/<issue-number>-short-description
- ๐ Contributing Guide
- ๐ฟ Git Guidelines
ยฉ 2026 SafeTrust. Released under the MIT License.
