- Site + API: https://tomo-together.com
- Web app: https://app.tomo-together.com
- APK download: https://tomo-together.com/apk
A cozy pixel-art friends app: real accounts, real hangouts, and real proof you showed up. It ships as an Expo/React Native Android app and web build backed by a small Express API.
UI styling uses the Sprout Lands UI Pack (Basic) by Cup Nooble: sprite panels,
buttons, icons, and pixel font in assets/ui and assets/fonts. Its license is
free for non-commercial use only, with no redistribution. Replace or license those
assets before commercial use.
- Accounts: self-hosted username/password sign-in. The server hashes
passwords with scrypt and issues an opaque bearer token per account, sent as
Authorization: Bearer. Accounts left over from the earlier Auth0 phase are claimed by setting a password on the first sign-in. - Profiles and friends: pick a username and avatar, search by username, send a request, and see equipped friends wandering through the yard.
- Hangouts: invite friends and use pairwise choices to narrow down an activity. Every pick tunes preference weights on the server.
- Proof: upload a photo and confirm every attendee pair by NFC. Completed hangouts become Memory Book polaroids and increase friendship vibe.
- Acorns and wardrobe: earn acorns through friendship levels and spend them on avatar items.
- Wallet and staking: the separately gated crypto service uses Unifold treasury custody for Base USDC and MongoDB Atlas for durable ledger, withdrawal, event, and idempotency state. Hangout no-shows can forfeit a stake to attendees. This moves real value when enabled.
- Music:
Tomo Yard.mp3loops quietly while the app is active. The global Music on/off control persists the preference. Web playback starts after the first user gesture because browsers block unsolicited autoplay.
- Main API and marketing site: https://tomo-together.com
- Expo web app: https://app.tomo-together.com
- APK download: https://tomo-together.com/apk
- Legacy hostnames
ht6.icinoxis.net/ht6-app.icinoxis.net301-redirect to the domains above; API routes still answer on the old host for installed APKs. - Main Azure App Service:
ht6-tomoyard - Crypto Azure App Service:
ht6-tomoyard-crypto - Atlas databases on the dedicated
tomo-yardcluster:tomoyard(main social data) andht6_crypto(crypto ledger), each with its own least-privilege user
Terraform in infra/ manages Azure and Cloudflare. External crypto credentials
live in Azure Key Vault ht6tomoyardkv4831; no secret value belongs in Terraform,
Git, an EXPO_PUBLIC_* variable, or chat. The crypto feature is enabled only when
Terraform's crypto_enabled input adds CRYPTO_API_URL to the main service. Keep
that input false until the crypto deployment passes /ready and the non-monetary
acceptance checks.
Read the production runbook before applying infrastructure, rotating credentials, or enabling crypto. It contains the Atlas inventory, Key Vault secret names, two-phase deployment, checks, rollback, and rotation procedures.
CI/CD on push to main uses:
.github/workflows/deploy-server.ymlfor the main API and marketing site;.github/workflows/deploy-web.ymlfor the Expo web build;.github/workflows/build-apk.ymlfor the signed Android APK; and.github/workflows/deploy-crypto.ymlfor the separately tested crypto service.
The crypto workflow needs AZURE_CRYPTO_WEBAPP_PUBLISH_PROFILE. APK signing needs
ANDROID_KEYSTORE_BASE64 and ANDROID_KEYSTORE_PASSWORD; preserve the original
keystore because APK updates must keep the same signature. No public auth-provider
build variables are required — authentication is served by the app's own API.
Main API (needs a MongoDB; tests bring their own in-memory instance):
docker run -d -p 27017:27017 mongo:7 # or any MongoDB at MONGODB_URI
cd server
npm install
npm test
node index.jsCrypto service:
cd custody
npm install
cp .env.example .env
# Fill the local ignored file; use CRYPTO_STORE_BACKEND=json only for local development.
npm test
npm startStart the main API with CRYPTO_API_URL=http://localhost:8787 and the same
32-or-more-character CRYPTO_SERVICE_TOKEN on both processes. Production refuses
the JSON store and requires Atlas.
The product cash-out minimum is 20 USDC (20000000 base units). A withdrawal's
Idempotency-Key and exact payload must be reused after an uncertain response;
generating a fresh key can represent a new money operation. Automatic real-USDC
grants, raw balance adjustments, and treasury-funded event bonuses are disabled in
production unless their exact dangerous opt-in is set to true.
npm install
npx expo run:androidThe NFC pieces (HCE service, TomoHce bridge module, manifest entries, and release
signing) are generated by plugins/withTomoHce.js, so npx expo prebuild can
regenerate the ignored android/ directory. The NFC HCE native module requires a
development or standalone build; do not use Expo Go for the NFC exchange test.
- Scan uses
react-native-nfc-managerwith IsoDep and AIDF0544F4D4F31. - Show uses a custom generated Android
HostApduService. - Phones without NFC get a clear message, and the emulator has no NFC radio.
- The radio exchange still needs two physical phones even when API paths pass.