README.md
g1t for Android and iOS
The phone app: Expo (SDK 57, React Native 0.86) with Expo Router, in TypeScript. Chat, Code and Customize, signed in with g1t's own OAuth. Android is the first target. What it does for people is in the guide, The phone app.
Layout
app.config.ts name, ids and icons per build variant (APP_VARIANT)
eas.json EAS Build profiles: development, preview, production
metro.config.js one React for the app in the monorepo
scripts/icons.mjs draws the icons from packages/theme/mark.svg
src/
app/ the screens (Expo Router: one file, one route)
sign-in.tsx Sign in with g1t, an access token, or another server
oauth/callback.tsx where the site sends sign-in back (g1t://oauth/callback)
(app)/ signed in: the tabs
chat/ conversations, and one conversation, live
code/ repositories, pull requests, issues, diffs
customize/ Skills, Connectors, Plugins
you/ workspaces, how you signed in, sign out
+native-intent.tsx g1t:// links to screens (lib/links.ts)
components/ the building blocks, themed (ui.tsx), and Code's
lib/
server.ts which g1t: site and API addresses
oauth.ts OAuth 2.1 with PKCE against the server's metadata
credentials.ts the server and credentials, in the phone's keystore
client.ts every request: refresh, errors, the site or the API
session.tsx who is signed in, to which workspace
chat.ts, code.ts the data for Chat and Code (TanStack Query)
customize.ts Customize's listings, from @g1t/contracts' catalogs
theme/ packages/theme/tokens.css's colours, for React NativeShared types and catalogs come from @g1t/contracts (by subpath, such as
@g1t/contracts/oauth, so the app does not bundle every module).
Customize's three parts are defined there too (customize.ts), so the
site can move the Marketplace onto the same model.
How it talks to g1t
| What | Where | How it is signed in |
|---|---|---|
| Who you are, your workspaces | GET {api}/user | Authorization: Bearer g1t_… |
| Code | {api}/repos… REST | the same |
| Chat | {site}/<workspace>/-/chat/api and its live socket, as the site's own pages use them | the same header, which the site takes only from a token with Use the website as you; the socket gets a ticket from {site}/-/live/ticket first |
| Sign-in | {api}/.well-known/oauth-authorization-server, {site}/oauth/authorize, {api}/oauth/token | OAuth 2.1, PKCE (S256), public client |
The OAuth client is not registered anywhere: its id encodes its name and
redirect (encodeOAuthClient), so g1t for Android with
g1t://oauth/callback is the same client on every phone. Access tokens
last 30 days and are refreshed a minute before they expire; a refresh
token works once, so lib/client.ts shares one refresh between
requests.
Open: Chat with the app's OAuth sign-in
OAuth tokens are made without the website permission
(services/identity/src/oauth.rs → issue_oauth_tokens), and Chat is
only on the site, so an OAuth sign-in reaches Code but not Chat. The
app says so on Chat and offers an access-token sign-in, which works today
with a token that has Use the website as you. The server needs one of
these before the app ships:
- Chat in the REST API (recommended):
GET/POST /workspaces/:ws/chat…inapps/apiwithchat:readandchat:writescopes, calling the chat service as the site does, and a socket ticket the API can mint. It is what the API and MCP will want anyway; the app moves its three calls inlib/chat.tsover. - Let the site take OAuth tokens for Chat's routes alone:
website-token.tsaccepts a token withoutwebsiteon/<ws>/-/chat/api,/<ws>/-/chat/liveand/-/live/ticketwhen it has newchat:*scopes. Smaller, but it widens what the site accepts.
Turning website on for every OAuth token is not one of them: it would
let any approved application use the whole site as the person.
Run it
You need Node 22 (the repository's), and for Android, Android Studio with an emulator or a phone with USB debugging.
npm install # at the repository's root: one workspace
cd apps/mobile
npx expo run:android # builds a development app and starts Metroexpo run:android makes the native project (android/, ignored by git)
and installs g1t (dev). After the first build, npx expo start and
the installed app are enough until a native module changes.
Expo Go works for screens that need nothing native beyond what Go has,
and sign-in comes back to Go's own exp:// address (allowed as a
redirect). g1t:// links need a development build.
Checks, which CI runs from the root (npm run typecheck, npm test):
npm run typecheck -w @g1t/mobile
npm test -w @g1t/mobile # lib/*.test.ts under NodeSet up EAS and ship Android
Once, by someone with access to the Expo account and Google Play:
npm install -g eas-cli, theneas login.- In
apps/mobile,eas init. It makes the EAS project and prints its id: put it inEAS_PROJECTinapp.config.ts(the config is code, soeas initcannot write it there). SetEAS_OWNERin your shell if the project belongs to an organization. eas build --profile preview --platform android. EAS makes and keeps the upload keystore the first time; say yes. The build ends with a link and a QR code to install the APK.- In the Google Play Console, create
the app with the package
sh.g1t.app, and turn on Play App Signing. npm run build:androidmakes the production App Bundle. Upload the first one by hand on Play's Internal testing track: Google requires that before its API accepts uploads.- For
eas submit, make a Google Cloud service account with access to the app in the Play Console, download its JSON key, and add it to the EAS project's credentials (eas credentials). From then on,eas submit --platform android --latestsends a build to internal testing as a draft (eas.json'ssubmit.production).
iOS is the same with --platform ios, an Apple Developer account and the
bundle id sh.g1t.app; EAS makes the certificates.
Before the first store release
- Chat with OAuth, above.
- Push notifications:
expo-notifications, and inservices/notifya subscription kind for Expo push tokens (or FCM and APNs) beside Web Push, registered from the app after sign-in. - App Links:
https://g1t.sh/.well-known/assetlinks.jsonnamingsh.g1t.appand Play's signing certificate's SHA-256, then anintentFiltersentry withautoVerifyinapp.config.ts, so g1t.sh links open the app. iOS needsapple-app-site-associationthe same way. - The brand's faces: TTF copies of Hanken Grotesk, Bricolage
Grotesque and IBM Plex Mono (packages/theme ships woff2), loaded with
expo-font. - Agents' faces:
components/ui.tsx'sAvatardraws a letter for an agent; the site'sAgentFaceshould be shared and drawn here. - The store listing: screenshots, a privacy policy URL, and Play's data safety form (the app keeps the server and credentials in the keystore, and sends nothing anywhere but the g1t it is pointed at).
Notes
- React is pinned. The site uses a newer React than React Native 0.86
allows, so the app has its own
react(19.2.3) andmetro.config.jsresolves everyreactimport to it.expo-doctorreports the root's copy as a duplicate; that is expected here, and the bundle has one. - Icons are drawn from the mark:
npm run icons -w @g1t/mobile. - Versions:
versioninapp.config.tsis what people see; EAS counts build numbers itself (appVersionSource: remote). Over-the-air updates go to builds of the sameversion(runtimeVersion).