TIDE docs
v1.0.0
Live demo Get help
● Set up · Settings and keys

Configuration
one document you host.

TIDE reads its operator settings from one JSON document. You write it with the operator panel of your own operator build, then host it as one file that every user build reads. Three build settings exist: your domain, the URL of that file and, for your operator build only, a passcode.

01Two places, one rule

WhereWhat goes thereChange needs
Build setting --dart-define=TIDE_HOST=…Your domain. Site, docs, terms and privacy links are built from it.A new build
Build setting --dart-define=TIDE_CONFIG_URL=…The https URL of your hosted configuration file. Empty = compiled defaults (no fee).A new build (once)
Build setting --dart-define=TIDE_OPERATOR_PASSCODE=…Your operator build only: adds the operator panel behind this passcode. Never set it in the build you give users.A new build
Operator config documentFee, feature switches, names, links, RPC endpoint and API keysEdit in your operator build → Copy as JSON → replace the hosted file. No rebuild.
No .env file

TIDE has no .env. Three --dart-define values configure it: TIDE_HOST (lib/core/config/app_endpoints.dart, with the optional overrides TIDE_TERMS_URL, TIDE_PRIVACY_URL and TIDE_SUPPORT_EMAIL), TIDE_CONFIG_URL (lib/core/config/remote_config_fetcher.dart) and TIDE_OPERATOR_PASSCODE (lib/core/config/operator_access.dart). Keys are never compiled in: RuntimeKeys.assertShipsKeyless() runs at start-up and fails a debug build if one ever is.

02Your domain (TIDE_HOST)

Default: the placeholder app.example.com, which is not a real site. Pass your own host (no https://) on every run and build:

flutter run -d chrome --dart-define=TIDE_HOST=app.example.com
flutter build web --release --dart-define=TIDE_HOST=app.example.com
Derived valueResult for app.example.com
Sitehttps://app.example.com
Docs link in the apphttps://app.example.com/docs
Terms / privacyhttps://app.example.com/terms, /privacy

If you set Website, Terms URL or Privacy URL in the operator panel, the app uses those and ignores the derived value. The support address defaults to support@<TIDE_HOST>; set Support email in the panel (and so in your hosted file) to use another one. If your host has no /terms or /privacy page, build with --dart-define=TIDE_TERMS_URL=… and --dart-define=TIDE_PRIVACY_URL=…; --dart-define=TIDE_SUPPORT_EMAIL= (empty) shows no support address at all. tool/build_web_demo.sh uses all three for the public demo.

03The layers

Weakest first. The strongest layer that supplies a value wins that value, leaf by leaf (lib/core/config/config_layers.dart). A hosted value always outranks a value stored on a device.

LayerWhat it is
1. Built-in defaultAdminConfigModel.defaults, compiled in. No fee, no copy execution, the Realised tab off.
2. Operator panelWhat was saved on this device. Only in an operator build (TIDE_OPERATOR_PASSCODE); a user build never reads it. Stored sparse: only values you changed.
3. Cached remote documentThe last hosted document this device fetched. Used only when a fetch fails.
4. Remote documentThe file at TIDE_CONFIG_URL, fetched at start-up (how).
5. --dart-defineFixed at build time and shown read-only.

Operator panel → In force right now lists every value the app is running on and which layer it came from.

TIDE settings screen: appearance, notification switches, region and copy execution, network
The settings a user controls on their own device. Operator settings live one screen deeper, in the operator panel.

04The document

This is the full shape, with the shipped defaults. Operator panel → Copy as JSON gives you the same shape with your values (only the ones you changed).

{
  "config_version": 1,
  "app_name": "TIDE",
  "tagline": "Swap on Solana. See what other wallets did.",
  "support_email": "",
  "website_url": "",
  "terms_url": "",
  "privacy_url": "",
  "announcement": "",
  "min_app_version": "",
  "default_slippage_pct": 1.0,
  "admin_locked": false,
  "flags": {
    "swap_enabled": true,
    "feed_enabled": true,
    "watchlist_enabled": true,
    "profiles_enabled": true,
    "realised_standings_enabled": false,
    "push_enabled": false,
    "evm_chain_enabled": false,
    "copy_execution_enabled": false
  },
  "fee": {
    "fee_enabled": false,
    "fee_bps": 0,
    "min_fee_usd": 0,
    "solana_fee_wallet": "",
    "evm_fee_wallet": ""
  },
  "keys": {
    "helius_api_key": "",
    "zerox_api_key": "",
    "solana_rpc_url": "",
    "evm_rpc_url": ""
  },
  "extra_restricted_regions": []
}

An unknown flag key is ignored. A region code that is not two letters is dropped. Every rate is clamped to its code cap on the way in (caps).

05Feature switches

KeyDefaultWhat it does
swap_enabledonSwap screen
feed_enabledonFeed of followed wallets
watchlist_enabledonFollowing wallets
profiles_enabledonProfile of a followed wallet
realised_standings_enabledoffThe Realised tab: what each followed wallet settled, listed in follow order, never ranked. Off removes the tab and every route into it.
push_enabledoffPush registration, if you wire a token provider (push)
evm_chain_enabledoffNot active in this version Base is scaffolded but no quote source is wired, so the switch has no effect.
copy_execution_enabledoffCopy execution module (read first)

06Endpoints and keys

Key in keysPurposeGet it at
solana_rpc_url RecommendedYour Solana RPC endpoint. Replaces the public one, and makes SPL balances load on the web build.Any Solana RPC provider
helius_api_keyNot active in this version Stored and shown as set, but no network call reads it in 1.0.0. To use Helius, paste the full URL https://mainnet.helius-rpc.com/?api-key=<your-key> into solana_rpc_url instead.helius.dev
zerox_api_keyNot active in this version Reserved for the EVM chain, which has no quote source.
evm_rpc_urlNot active in this version Same reason. Not shown in the panel.
A hosted document is public

Anything you put in a hosted document can be read by anyone who finds its URL, and every app build can read it. Use RPC URLs and keys that are restricted in your provider's dashboard (allowed origins, rate limits). A support dump made with toJsonRedacted() shows keys only as [set].

07Publishing your configuration

Your users' builds have no operator panel. They read one file you host. No code change is needed.

  1. Make your operator buildflutter run -d chrome --dart-define=TIDE_OPERATOR_PASSCODE=choose-your-own (Operator panel).
  2. Set everythingIn the operator panel, then press Copy as JSON.
  3. Host itSave the text as a file, for example tide-config.json, on any HTTPS static host you control (your host's fees apply). Only https URLs are accepted.
  4. Build the user app with the URL
    flutter build appbundle --release \
      --dart-define=TIDE_HOST=app.yourdomain.com \
      --dart-define=TIDE_CONFIG_URL=https://app.yourdomain.com/tide-config.json
    The same two defines go on flutter build ipa and flutter build web. Do not pass TIDE_OPERATOR_PASSCODE here.
  5. Change settings laterEdit in your operator build, Copy as JSON, replace the hosted file. Every install reads it on its next start and caches the last good copy.

Optional: "admin_locked": true in the hosted file makes the panel of an operator build read-only and ignores its stored values, which is useful when several people hold operator builds. Firestore or Remote Config can replace the file: pass your own fetchRemote to AdminConfigService in lib/main.dart (see docs/FIREBASE-SETUP.md in the source).

08Push notifications and Firebase (optional)

TIDE works without Firebase and ships no Firebase files. Push is a keyless hook: PushNotificationService in lib/core/notifications/push_notification_service.dart accepts a tokenProvider you supply. To use Firebase: create your own project, add the firebase_core and firebase_messaging packages, run flutterfire configure, initialise Firebase in lib/main.dart, and pass tokenProvider: () => FirebaseMessaging.instance.getToken(). Sending pushes needs a server or the Firebase console; TIDE includes none. These steps are from docs/FIREBASE-SETUP.md and were not run for these docs.

If you store config in Firestore, keep it read-only for clients and never store anything that links a person or device to a wallet address.

09Where data is stored

DataWhere
Seed phrase and keysiOS Keychain / Android Keystore, encrypted by TIDE with AES-256-GCM first. Web: browser storage, sealed with keys derived from the user's vault passphrase (PBKDF2), which is never stored
Operator panel valuesOn the device of an operator build only (AdminConfigStore)
Watchlist, notifications, preferences, copy rules, declared regionOn the device

There is no server and no database. Nothing is uploaded.