From 66b68104a42cd1c2ffd8d0f8f07c43f838e551e6 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 22 Jun 2026 15:32:32 +0000 Subject: [PATCH] docs: add co-located README docs for services/* modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Module-level documentation for the four service modules, at catalog + internals depth, cross-linked to the architecture suite: - services/opencode/README.md — OCP client: 5 public controllers, the SubmitIntent bidirectional handshake (IntentExecutor + BidirectionalStream Reference, with a sequence diagram), intents/actions, the solana/programs instruction builders, transactors, swaps, and exchange/VerifiedState; DI + the ed25519.shadow plugin; an "adding an intent" checklist. - services/flipcash/README.md — Flipcash backend client: the 14 controllers, models/UserManager/AuthState, signing + request lifecycle, event streaming, DI/channels, and an "adding an RPC" checklist. - services/opencode-compose/README.md — LocalExchange + ExchangeStub bindings. - services/flipcash-compose/README.md — intended Compose-bindings layer, explicitly flagged as a shell (no src yet). Adds a "Module-level docs" pointer from 04-networking to the four READMEs. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01JRVfsXp4HDrDMy7Pbmw9fD --- docs/architecture/04-networking.md | 12 ++ services/flipcash-compose/README.md | 25 +++++ services/flipcash/README.md | 107 ++++++++++++++++++ services/opencode-compose/README.md | 29 +++++ services/opencode/README.md | 164 ++++++++++++++++++++++++++++ 5 files changed, 337 insertions(+) create mode 100644 services/flipcash-compose/README.md create mode 100644 services/flipcash/README.md create mode 100644 services/opencode-compose/README.md create mode 100644 services/opencode/README.md diff --git a/docs/architecture/04-networking.md b/docs/architecture/04-networking.md index 514b5b00c..4806f7b37 100644 --- a/docs/architecture/04-networking.md +++ b/docs/architecture/04-networking.md @@ -130,6 +130,18 @@ Tokens are short-lived and passed per request. transitions, and `shareIn`s a single connectivity flow. It's published app-wide as `LocalNetworkObserver`. +## Module-level docs + +Each service module carries its own `README.md` with the full controller catalog and +internals: + +- [`services/opencode`](../../services/opencode/README.md) — OCP: intents, the + `SubmitIntent` handshake, transactors, Solana programs, swaps, exchange. +- [`services/flipcash`](../../services/flipcash/README.md) — accounts, chat, + contacts, profiles; signing path and event streaming. +- [`services/opencode-compose`](../../services/opencode-compose/README.md) · + [`services/flipcash-compose`](../../services/flipcash-compose/README.md) — Compose bindings. + ## Why this matters The four-layer split means a proto or transport change is absorbed at the Api/ diff --git a/services/flipcash-compose/README.md b/services/flipcash-compose/README.md new file mode 100644 index 000000000..0722a72a6 --- /dev/null +++ b/services/flipcash-compose/README.md @@ -0,0 +1,25 @@ +# services/flipcash-compose + +The intended **Compose-bindings layer** over [`services/flipcash`](../flipcash/README.md), +mirroring what [`services/opencode-compose`](../opencode-compose/README.md) does for +opencode. + +> Namespace `com.flipcash.services.flipcash.compose`. + +> **Status: currently a shell.** This module has **no `src/` yet** — only a +> `build.gradle.kts` that `api(...)`-exports `:services:flipcash` and +> `:services:opencode-compose`. It exists so feature/UI code can depend on a single +> Compose-aware entry point for the Flipcash backend. + +## What belongs here + +Compose-facing bindings for `:services:flipcash` — the same shape as +`opencode-compose`'s `LocalExchange`: `staticCompositionLocalOf` handles and +`@Composable` providers/`remember*` helpers that expose Flipcash controllers/state to +the composition tree (see the CompositionLocal pattern in +[02 — State & dependency injection](../../docs/architecture/02-state-and-dependency-injection.md)). +Keep the Compose runtime here, out of the Compose-free `:services:flipcash` core. + +## See also + +- [`services/flipcash`](../flipcash/README.md) · [`services/opencode-compose`](../opencode-compose/README.md) · [02 — State & DI](../../docs/architecture/02-state-and-dependency-injection.md) diff --git a/services/flipcash/README.md b/services/flipcash/README.md new file mode 100644 index 000000000..9b983c81a --- /dev/null +++ b/services/flipcash/README.md @@ -0,0 +1,107 @@ +# services/flipcash + +The Android client for the **Flipcash app backend** — accounts/auth, profiles, +contacts, chat & messaging, activity feed, moderation, push, settings, and +third-party (on-ramp) integration. It builds on [`services/opencode`](../opencode/README.md) +(re-exported via `api(...)`) for the core gRPC infrastructure and Solana models. + +> Namespace `com.flipcash.services.*`. Layering (API → Service → Repository → +> Controller) is described in +> [04 — Networking](../../docs/architecture/04-networking.md); the auth state machine +> and account model in [06 — Payments & operations](../../docs/architecture/06-payments-and-operations.md). + +```mermaid +graph TD + Feature["feature / shared coordinator"] + Ctrl["controllers/* (public)"] + Repo["repository/* (interfaces)"] + IRepo["internal/repositories/* (+ mappers)"] + Svc["internal/network/services/* (Result + typed errors)"] + Api["internal/network/api/* (gRPC stubs + signing)"] + Backend["Flipcash backend"] + + Feature --> Ctrl --> Repo --> IRepo --> Svc --> Api --> Backend +``` + +## Public API — controllers + +Fourteen controllers, each backed by a `repository/` interface (implemented by an +`internal/repositories/InternalXxxRepository`): + +| Controller | Area | +|------------|------| +| [`AccountController`](src/main/kotlin/com/flipcash/services/controllers/AccountController.kt) | Register, login, get user flags | +| `ProfileController` | Display name, profile, social-account linking | +| `ContactListController` | Contact CRUD; checksum / delta / full upload | +| `ContactVerificationController` | Email & phone verification | +| `ResolverController` | Contact resolution | +| `ChatController` / `ChatMessagingController` | Chat retrieval; send/receive, typing, pointers | +| `EventStreamingController` | **Streaming** chat updates (`Flow`) | +| `ActivityFeedController` | Activity feed | +| `ModerationController` | Text / image moderation | +| `PushController` | Push-token registration | +| `PurchaseController` | In-app-purchase acknowledgement | +| `SettingsController` | Locale / region settings | +| `ThirdPartyController` | On-ramp providers, liquidity pools | + +## Models, auth & state + +- **`models/`** — public domain types: `UserFlags` (server-driven account flags & + entitlements, e.g. `isStaff`, `enablePhoneNumberSend`), `UserProfile`, + the `chat/` models (`ChatMetadata`, `ChatMessage`, `ChatUpdate`, …), `Jwt`, + `QueryOptions`/`PagingToken`, and `Errors.kt` — **50+ sealed error hierarchies** + (`LoginError`, `RegisterError`, `SendMessageError`, …) over `CodeServerError` + ([14 — Error handling](../../docs/architecture/14-error-handling.md)). +- **`user/UserManager`** — the singleton auth **state machine** (`AuthState`: + `Unknown → Onboarding → Authenticating → Ready → LoggedOut`), exposing + `state: StateFlow`, the `mnemonic`/`entropy`, `accountCluster`, `userFlags`, + `profile`. It's the source of truth for "am I logged in?" across the app + ([06](../../docs/architecture/06-payments-and-operations.md)). +- `modals/ModalManager`, `persistence/` (`DataSource` abstractions), `validators/`. + +## DI & channels + +`inject/FlipcashModule` (`internal`) provides two channels into `SingletonComponent`: + +- `@FlipcashManagedChannel` — unary (`idleTimeout 5m`, `keepAliveWithoutCalls=false`). +- `@FlipcashManagedStreamingChannel` — streaming (`keepAliveWithoutCalls=true`). + +Both target the Flipcash base URL with the `LoggingClientInterceptor`, configured via +`@FlipcashProtocol ProtocolConfig`. It also **binds** the 14 repository interfaces to +their `Internal*` implementations. The module's `build.gradle.kts` `api(...)`-exports +`:services:opencode` and `:libs:network:jwt`. + +## Deep dive: signing & the request lifecycle + +Requests are signed with the owner's **Ed25519** key at the API boundary. +`internal/network/extensions/` holds the helpers: `sign(owner)` / +`authenticate(owner)` (build `Common.Signature` / `Common.Auth`) plus +`LocalToProtobuf` / `ProtobufToLocal` converters between domain and generated types. +A typical call — e.g. `AccountController.register` → `AccountService.register` → +`AccountApi.register` — builds the protobuf request, validates it (`protovalidate`), +signs it, sends it on the unary channel, and folds the response into a +`Result` with a typed error on failure (`RegisterError.*`). + +## Deep dive: event streaming + +`EventStreamingController` exposes chat updates as a `Flow` over the +streaming channel. The stream is opened/closed explicitly and its **lifecycle is the +caller's responsibility** (a coordinator owns the scope and re-opens on +foreground/reconnect). + +## Adding an RPC + +1. **Proto** — update `definitions/flipcash`, regenerate + ([13](../../docs/architecture/13-protobuf-and-codegen.md)). +2. **Api** — add the call to `internal/network/api/XxxApi` (build request, sign, validate). +3. **Service** — map the response to `Result` + a typed error in + `internal/network/services/XxxService`. +4. **Repository** — add to the `repository/` interface and `internal/repositories/InternalXxxRepository` + (+ a mapper if the model needs translating). +5. **Controller** — expose it on the public `controllers/XxxController`; bind any new + repository in `FlipcashModule`. + +## See also + +- [04 — Networking](../../docs/architecture/04-networking.md) · [06 — Payments & operations](../../docs/architecture/06-payments-and-operations.md) · [13 — Protobuf & codegen](../../docs/architecture/13-protobuf-and-codegen.md) · [14 — Error handling](../../docs/architecture/14-error-handling.md) +- Core infra & models: [`services/opencode`](../opencode/README.md) · Compose bindings: [`services/flipcash-compose`](../flipcash-compose/README.md) diff --git a/services/opencode-compose/README.md b/services/opencode-compose/README.md new file mode 100644 index 000000000..291fb89ae --- /dev/null +++ b/services/opencode-compose/README.md @@ -0,0 +1,29 @@ +# services/opencode-compose + +A thin Compose-bindings layer over [`services/opencode`](../opencode/README.md). It +re-exports the opencode API (`api(:services:opencode)`) and surfaces the things +features read from the composition tree rather than inject directly. + +> Namespace `com.getcode.opencode.compose`. See the CompositionLocal injection +> pattern in [02 — State & dependency injection](../../docs/architecture/02-state-and-dependency-injection.md). + +## What's here + +- **`LocalExchange`** (`Exchange.kt`) — `staticCompositionLocalOf` that + exposes the OCP [`Exchange`](../opencode/README.md) (rates, preferred currency) to + Compose. Provided in `MainActivity`'s `CompositionLocalProvider`; read with + `LocalExchange.current`. +- **`ExchangeStub`** (`ExchangeStub.kt`) — an inert `Exchange` (empty/identity rates) + used as the default when no real `Exchange` is provided (previews, tests). + +## Why a separate module + +`:services:opencode` is Compose-free (it's a plain library). This module adds the +Compose dependency and the `Local*` bindings, keeping the Compose runtime out of the +core service module. Features depend on `:services:opencode-compose` (often +transitively via `:services:flipcash-compose`) to get both the API and its ambient +handles. + +## See also + +- [`services/opencode`](../opencode/README.md) · [02 — State & DI](../../docs/architecture/02-state-and-dependency-injection.md) · [06 — Payments & operations](../../docs/architecture/06-payments-and-operations.md) diff --git a/services/opencode/README.md b/services/opencode/README.md new file mode 100644 index 000000000..34706a53f --- /dev/null +++ b/services/opencode/README.md @@ -0,0 +1,164 @@ +# services/opencode + +The Android client for the **Open Code Protocol (OCP)** — the gRPC backend that +handles money movement: transactions/intents, swaps, exchange rates, accounts, and +the Solana transaction construction that backs them. This is the deeper of the two +service modules; `services/flipcash` wraps the Flipcash app backend and depends on +this one. + +> Namespace `com.getcode.opencode.*`. For the layering this module follows +> (API → Service → Repository → Controller) see +> [04 — Networking](../../docs/architecture/04-networking.md); for the currency model +> (USDF, launchpad tokens, `AccountCluster`) see +> [06 — Payments & operations](../../docs/architecture/06-payments-and-operations.md). + +```mermaid +graph TD + Feature["feature / shared coordinator"] + Ctrl["controllers/* (public, stateful)"] + Repo["repositories/* (interfaces)"] + Svc["internal/network/services/*"] + Exec["internal/network/executors/* (bidi)"] + Api["internal/network/api/* (gRPC stubs)"] + Intents["solana/intents/* + internal/solana/programs/*"] + Backend["OCP backend + Solana"] + + Feature --> Ctrl --> Repo --> Svc --> Api --> Backend + Svc --> Exec --> Api + Exec --> Intents +``` + +## Public API — controllers + +Features consume **controllers** (and the `Exchange` interface); everything under +`internal/` is off-limits. + +| Controller | Purpose | Key surface | +|------------|---------|-------------| +| [`TransactionController`](src/main/kotlin/com/getcode/opencode/controllers/TransactionController.kt) | Submits intents (transfer, withdraw, remote send/receive, buy/sell), tracks limits | `submitIntent(...)`, `buy/sell/withdraw`, `limits: StateFlow`, implements [`TransactionOperations`](src/main/kotlin/com/getcode/opencode/controllers/TransactionOperations.kt) | +| [`AccountController`](src/main/kotlin/com/getcode/opencode/controllers/AccountController.kt) | Account clusters, linking/unlinking, account-state polling | active-cluster + account-list `StateFlow`s | +| [`TokenController`](src/main/kotlin/com/getcode/opencode/controllers/TokenController.kt) | **Stateless** network gateway for token metadata / balances | flows only; caching is the consumer's job (the `TokenCoordinator`) | +| [`CurrencyController`](src/main/kotlin/com/getcode/opencode/controllers/CurrencyController.kt) | Rates, currencies, live/historical mint data | `streamLiveMintData()`, rate flows | +| [`MessagingController`](src/main/kotlin/com/getcode/opencode/controllers/MessagingController.kt) | P2P bill messaging streams (give/grab/claim) | open/poll/ack message stream | + +Repositories (`repositories/`) are the interface seam beneath the controllers: +`Transaction`, `Account`, `Currency`, `Messaging`, `Swap`, `Event` — each implemented +by an `Internal*Repository` under `internal/domain/repositories`. + +## Models + +Public domain models live under `model/` (see +[06](../../docs/architecture/06-payments-and-operations.md) for the economic model): + +- **`model/financial/`** — `MintMetadata` (alias `Token`; `Token.usdf`), + `LaunchpadMetadata` (USDF-backed bonding-curve fields), `Fiat`/`LocalFiat`/ + `VerifiedFiat`, `Rate`, `Currency`/`CurrencyCode`, `Fee`, `Limits`, `Distribution`. +- **`model/accounts/`** — `AccountCluster` (authority + timelock + per-token deposit + addresses), `TimelockDerivedAccounts`, `AccountType`, `GiftCardAccount`, `PoolAccount`. +- **`model/transactions/`** — `TransactionMetadata`, `SwapMetadata`/`SwapState`, + `ExchangeData` (`Verified`/`Unverified`). +- **`model/core/`** — `OpenCodePayload` (the QR/rendezvous payload), `ID`. + +## DI & channels + +`inject/` provides three Hilt modules into `SingletonComponent`: + +- **`OpenCodeModule`** — `Exchange`, `ProtocolConfig` (`@OpenCodeProtocol`), and the + two gRPC channels: `@OpenCodeManagedChannel` (unary) and + `@OpenCodeManagedStreamingChannel` (keep-alive for streams), both built with + `AndroidChannelBuilder`/`OkHttpChannelBuilder` and the `LoggingClientInterceptor`. +- **`TransactorModule`** — transactors + `PayloadFactory`, `AccountClusterFactory`. +- **`SessionListenersModule`** — the `SessionListener` set (login/logout hooks). + +> **`ed25519.shadow` plugin:** the build applies `flipcash.android.ed25519.shadow`, +> which shades the native Ed25519 library so this module's crypto can't collide with +> other consumers' versions. It's the only module that needs it. + +## Deep dive: the `SubmitIntent` bidirectional handshake + +Money movement is a **bidirectional stream**, not a fire-and-forget RPC. The client +builds and signs the Solana transactions locally using server-provided parameters. +`IntentExecutor` (`internal/network/executors/`) drives it over a +`BidirectionalStreamReference` (`internal/bidi/`): + +```mermaid +sequenceDiagram + participant C as Client (IntentExecutor) + participant S as OCP server + C->>S: SubmitActions (intent id, actions, signature) + S->>C: ServerParameters (per-action nonces, params) + Note over C: apply params, build txns, sign locally + C->>S: SubmitSignatures + S->>C: Success (or Error) +``` + +On `Error`, `SubmitIntentError.typed(...)` maps the code to a typed failure; the +`InternalTransactionRepository` **retries on `StaleState` race conditions** +(`retryableOrThrow`, see [14 — Error handling](../../docs/architecture/14-error-handling.md)). + +### Intents & actions +An **intent** is an `IntentType` (`solana/intents/`) holding an `ActionGroup` of +`ActionType`s. Kinds (`internal/network/api/intents/`): `IntentTransfer`, +`IntentWithdraw`, `IntentRemoteSend`/`IntentRemoteReceive`, `IntentDistribution`, +`IntentCreateAccount`, `IntentStatefulSwap`/`IntentStatelessSwap`, `IntentFundSwap`. +Actions (e.g. `ActionPublicTransfer`, `ActionPublicWithdraw`, `ActionOpenAccount`, +`ActionFeePayment`) each construct their transaction from server parameters and +contribute Ed25519 signatures. + +### Solana programs +`internal/solana/programs/` (~33 builders) encode the on-chain instructions: +`TimelockProgram` (vault lifecycle), `VirtualMachineProgram` (VM swaps), +`TokenProgram`/`AssociatedTokenProgram` (SPL), `CurrencyCreatorProgram` +(`InitializeCurrency`/`BuyTokens`/`SellTokens`/`InitializePool`), +`CoinbaseStableSwapperProgram` (USDC↔USDF), `ComputeBudgetProgram`, `SystemProgram`, +`MemoProgram`. + +## Deep dive: transactors + +A **transactor** (`internal/transactors/`) is a coroutine-scoped state machine for a +multi-step, multi-RPC workflow that coordinates **messaging + an intent**. Lifecycle: +`with(...)` configures (token, amount, rendezvous payload), `start()` runs the flow, +`dispose()` cancels the scope. + +- `GiveBillTransactor` / `GrabBillTransactor` — the device-to-device cash-bill + exchange: advertise on the messaging stream, wait for the counterpart, then submit + the transfer/remote-receive intent. +- `SendGiftCardTransactor` / `ReceiveGiftCardTransactor` — gift-card (link) flow. +- `Transactor` is the base (provides `logAndFail`); `PayloadFactory` builds the + `OpenCodePayload`, `AccountClusterFactory` derives clusters. + +## Deep dive: swaps + +Buy/sell of a launchpad currency against USDF runs through `StatefulSwapExecutor` +(escrowed state machine: `SwapState` CREATED→FUNDED→COMPLETED) or +`StatelessSwapExecutor` (atomic, signed up-front). `SwapRepository`/`SwapService` +expose it; `SwapFundingSource` selects how the swap is funded. + +## Deep dive: exchange & verified rates + +`exchange/Exchange` (impl `internal/exchange/OpenCodeExchange`) streams and caches +rates and tracks the preferred currency. Every payment carries a +**cryptographically-signed rate proof**: `VerifiedProtoManager` seals a `VerifiedState` +into an immutable `VerifiedFiat` (via `VerifiedFiatCalculator`), so the amount and the +rate it was computed at can't drift. See +[06](../../docs/architecture/06-payments-and-operations.md). + +`providers/` exposes `SessionListener` (the login/logout hook coordinators implement) +and `TokenMetadataProvider`. + +## Adding an intent / RPC + +1. **Proto** — update `definitions/opencode` and regenerate + ([13](../../docs/architecture/13-protobuf-and-codegen.md)). +2. **Intent** — add an `IntentType` + its `ActionType`s under + `internal/network/api/intents/`; add any new `internal/solana/programs/` instruction. +3. **Api/Service/Executor** — extend `TransactionApi` and `TransactionService` + (or add an executor for a new streaming flow). +4. **Repository** — add the method to the repository interface + `Internal*Repository` + (with typed-error mapping / retry where relevant). +5. **Controller** — expose it on the public controller. + +## See also + +- [04 — Networking](../../docs/architecture/04-networking.md) · [06 — Payments & operations](../../docs/architecture/06-payments-and-operations.md) · [13 — Protobuf & codegen](../../docs/architecture/13-protobuf-and-codegen.md) · [14 — Error handling](../../docs/architecture/14-error-handling.md) +- Sibling: [`services/flipcash`](../flipcash/README.md) · Compose bindings: [`services/opencode-compose`](../opencode-compose/README.md)