A discord-like nostr client based on the idea of "relays as groups". Supports NIP 29 groups, chat, DMs, threads, calendars, classifieds, zap goals, articles, microblogging, and cross-posting between different contexts.
-`VITE_PLATFORM_RELAYS` - A comma-separated list of relay urls that will make flotilla operate in "platform mode". Disables all space browse/add/select functionality and makes the first platform relay the home page.
**Defaults**
-`VITE_DEFAULT_PUBKEYS` - A comma-separated list of hex pubkeys for bootstrapping web of trust
-`VITE_DEFAULT_SPACES` - A comma-separated list of relay urls that new users will be automatically joined to on signup. Each one may optionally include an invite code, delimited by `|`, e.g. `my.space.com|CODE`.
These values **won't** be used for a built version. Instead, env variables should be provided to `scripts/build.sh` directly or to the built container.
If you're deploying a custom version of flotilla, be sure to remove the `plausible.coracle.social` script from `app.html`. This sends analytics to a server hosted by the developer.
`pnpm release --check` runs those checks and reports the plan without building anything. Naming
steps runs a subset — `pnpm release ios`, or `pnpm release apk gitea`. A step that fails stops the
run and prints the command to pick up from there.
| step | what it does |
| --- | --- |
| `web` | `scripts/build.sh`: web bundle, `cap sync`, generated icons and splash screens |
| `apk` | `assembleRelease` signed with the distribution key, renamed to the path in `zapstore.yaml` |
| `play` | `bundleRelease` signed with the upload key, uploaded to a Play track as a draft |
| `ios` | `cap build ios` to an archive and IPA, uploaded with `altool` |
| `desktop` | `package:desktop:*` for this OS |
| `gitea` | creates the release for the tag from the changelog, attaches the APK and any desktop packages |
| `zapstore` | `zsp publish zapstore.yaml` |
| `fdroid` | nothing to upload; F-Droid builds from the tag, see [fdroid/README.md](fdroid/README.md) |
Release notes come from the `CHANGELOG.md` section matching `package.json`'s version, so every
store shows the same text. The APK and zapstore share one artifact, whose path lives in
`zapstore.yaml`.
### Credentials
These go in `.env.local`, which is gitignored. `pnpm release --check` lists whichever are missing
along with how to get them.
| variable | what it is |
| --- | --- |
| `GITEA_TOKEN` | gitea access token with `write:repository`, from Settings → Applications |
| `ANDROID_KEYSTORE_PATH`, `ANDROID_KEYSTORE_PASSWORD`, `ANDROID_KEYSTORE_ALIAS` | the key APKs outside the app stores are signed with; it can never change without breaking updates |
| `PLAY_KEYSTORE_PATH`, `PLAY_KEYSTORE_PASSWORD`, `PLAY_KEYSTORE_ALIAS` | the Play upload key |
| `PLAY_SERVICE_ACCOUNT` | path to a service account json with the Release manager role, from Play Console → Setup → API access |
| `ASC_KEY_ID`, `ASC_ISSUER_ID`, `ASC_KEY_PATH` | App Store Connect API key with the App Manager role, from Users and Access → Integrations |
| `SIGN_WITH` | nostr key for zapstore: an nsec, a `bunker://` url, or `browser` |
Add `_ALIAS_PASSWORD` to either keystore prefix when the alias has its own password. `PLAY_TRACK`
(default `production`) and `PLAY_STATUS` (default `draft`) choose where a Play upload lands.
Keystores and API keys belong outside the repository; only their paths go in `.env.local`.
Gradle signs from those variables, so Android Studio still opens and builds the project without
them — it just produces an unsigned release build.