Skip to content
GenyCaster
Technology-independent architecture

Implement the core.
Choose your own stack.

A client can be written in any programming language. What matters is the boundary between canonical protocol data, capability contracts, providers, local persistence, signing and durable message delivery.

Contract-first Backend architecture

1. Canonical domain model

Define provider-independent entities before connecting any external APIs. A FID identifies a Farcaster user; a Cast represents a signed post; a Profile contains normalized user attributes; a Link encodes following relationships; and a MessageId identifies signed protocol messages. No entity should store provider-specific presentation fields as authoritative protocol state.

Illustrative canonical model — language-neutral JSON
{
  "profile": {
    "fid": 1234,
    "username": "example",
    "displayName": "Example user",
    "source": {
      "providerId": "reader-1",
      "contract": "snapchain-v1"
    }
  },
  "capabilities": {
    "profile.byFid": true,
    "feed.global": false,
    "posts.write": false
  }
}

This is an illustrative internal representation, not an actual Snapchain response or a published Genycaster JSON API.

2. Ports and capability contracts

Define interfaces by operation, not by vendor hostname. The application calls a normalized read port; each adapter maps the operation to its own wire protocol. A writer port accepts canonical signed messages and is never silently substituted by a read API.

PortContractTypical implementation
ReadGatewayProfile, cast, FID-scoped feed, follow links and paged resultsRaw Snapchain adapter
IndexedGatewaySearch, derived timelines, aggregate counts if suppliedOptional indexed API adapter
WriteGatewaySubmit pre-signed message, observe confirmationValidator / write-capable RPC
LocalStoreRead snapshots, upsert normalized entities, persist cursorsTransactional persistence
SignerSign canonical messages with authorized client keyDevice-owned signing boundary

A provider advertises capabilities explicitly. Do not guess compatibility from a successful network ping or a familiar brand name.

3. Capability-based routing algorithm

If no allowed provider satisfies the request, return an explicit unsupported_capability. Do not leak an FID or other user data to an unapproved fallback. Health, authorization and support are separate dimensions.

4. Cache, synchronization and cursors

Read the most recent local snapshot first, then schedule bounded remote refreshes. Persist canonical records and freshness metadata. A cursor is scoped to the provider ID, endpoint identity, query, filters and contract version; opaque cursors from different services must never be combined.

Cursor binding — illustrative record
{
  "route": {
    "providerId": "reader-1",
    "contract": "snapchain-v1",
    "endpointFingerprint": "opaque-bound-identifier"
  },
  "query": { "capability": "casts.byFid", "fid": 1234 },
  "cursor": "opaque-value-from-that-provider",
  "fetchedAt": "ISO-8601 timestamp"
}

Storage technology is an implementation choice: an embedded database, IndexedDB, server-side relational storage or another transactional engine can all work if they preserve stable IDs, provenance and atomic outbox updates.

5. Signed writes and durable delivery

Keep the signer boundary separate from readers. Obtain an authorized app key through a trusted custody flow, sign a canonical protocol message locally, persist a delivery record and pin the chosen validator endpoint identity before attempting to send.

Track pending, acknowledged, uncertain, rejected and confirmed states distinctly. An HTTP timeout is not proof that a signed message was not accepted.

6. Contracts to test in every implementation

  1. Raw providers cannot claim derived capabilities they do not expose.
  2. Strict mode produces zero extra provider calls.
  3. Fallback enrichment preserves the primary user's FID and canonical identity.
  4. Changing readers invalidates route-bound cursors and keeps prior data provenance.
  5. Local cache returns a consistent snapshot while disconnected.
  6. A queued signed message never changes its selected writer implicitly.
  7. Unknown confirmation state does not cause unsafe duplicate publishing.
  8. Credentials and signer secrets are not logged or sent through read-provider routes.

Reference architecture: system diagram · provider contracts · self-hosting and node health.