Skip to content
Jinnie

Build an app for Jinnie.

One package, one manifest entry, one exposed screen. The host renders your default export inside its own scroll container, app bar, safe area and error boundary, so what you write is content rather than a screen.

1. Create the package

Copy an existing community app. packages/remit is the smallest useful template. A mini app is:

packages/hello/
  package.json         name: @jinnie/hello
  rspack.config.mjs    container name 'hello', exposes './App', devServer port
  babel.config.js      react-native preset
  react-native.config.js
  index.js             standalone entry (dev only)
  app.json
  src/App.tsx          the default export the host renders

The container name in rspack.config.mjs and the id in the host registry must match. That pairing is what resolves hello/App at runtime.

2. Write the screen

import React from 'react';
import {Card, Body, useHost} from '@jinnie/sdk';

export default function HelloApp() {
  const host = useHost();
  return (
    <Card>
      <Body>
        Hello from {host.app.name}. {host.online ? 'Online.' : 'Offline.'}
      </Body>
    </Card>
  );
}

3. Register it with the host

Two edits in the host package: a port for the dev server, and one manifest entry.

// rspack.config.mjs — MINI_APPS
hello: 9014,
// src/registry.ts
{
  id: 'hello',
  name: 'Hello',
  description: 'My first mini app.',
  icon: IconGrid,
  tone: 'violet',
  kind: 'community',
  author: 'Your Name',
  version: '1.0.0',
}

The host loads hello/App through the federation runtime by name, so there is nothing else to declare. Add it to the dev dashboard and start everything.

The bridge: useHost()

The only way a mini app may talk to the host.

MemberTypeNotes
app{id, name, version}Your manifest identity
user{id, name} | nullnull until host sign-in ships
onlinebooleanLive connectivity
currencystring'PHP'
storageget / set / removeAsync. Namespaced to your app id
financeHostFinance?Core apps only. undefined for community apps
close()() => voidReturn to the launcher

HostFinance exposes the balance, the base currency, active accounts, normalised asset, liability, and net-worth totals, the read-only Wallet transactions, and four mutations:

addTransaction({type, amount, description, category, accountId?, billId?, billPeriod?})
transferBetweenAccounts({amount, description, fromAccountId, toAccountId})
updateTransaction(id, changes)
deleteTransaction(id)

Transfers are neutral to cash flow and move both linked balances. Linked account balances and bill-payment status are reconciled when an entry is edited or deleted. Mutations reject non-finite or non-positive amounts, blank descriptions, unknown or archived account ids, and same-account transfers. The bridge and the regression suite call the same implementation, so a test that passes is a statement about the app, not about a copy of it.

Public catalogs

Read-only platform metadata, safe for any app. Never a user’s balances.

import {
  catalogs, financialProviders, transactionCategories, billers, currencies,
  ProviderPicker, CategorySelect, BillerPicker, CurrencyPicker, AccountPicker,
} from '@jinnie/sdk';

Persist the id, not the display label, so copy and localisation can change safely. Every catalog picker ends with Add new; a name the user types there is saved once and offered in every picker of that kind from then on. Every category has a glyph, and institutions and billers draw a bundled logo when one exists and a monogram otherwise. Community apps may use the provider picker; the finance context stays withheld regardless.

UI kit

Everything in the SDK’s UI folder is exported from @jinnie/sdk: plain StyleSheet on top of the theme tokens, no UI framework, nothing a mini app needs to restyle.

GroupComponents
LayoutStack, Row, Card, CardTitle, SectionHeader, AppBar
ContentBody, Muted, Stat, Badge, Progress, InlineAlert, EmptyState, Skeleton
ControlsButton, IconButton, PressableCard, Input, Textarea, Label, Switch, Segmented, SearchInput, Select
TokensuseTheme, useIsDark, space, radius, tnum, toneColors, withAlpha

Rules

  1. Import only from @jinnie/sdk, react, and react-native. Never reach into another app’s package or into the host.
  2. No native dependencies. The host owns them. If you truly need one, it has to be added to the host as a shared singleton, which means a host release. That is the cost the architecture is designed to make visible.
  3. Offline-first. Anything you persist goes through host.storage or a store in the SDK. Any network call must degrade gracefully and disclose what it sends.
  4. No colour literals. useTheme() or a named tone. Both schemes must look deliberate.
  5. Every tap target is at least 44pt, and every icon-only control has a label. The SDK components enforce this; do not hand-roll around them.
  6. One sheet at a time. A select inside a sheet opens as a page of that sheet. Never render a sheet while another is open.
  7. English copy, professional tone, no emoji. Use the icon set.

Testing

Finance logic lives in the SDK and runs under plain Node with in-memory storage shims. Add cases there for anything that touches money. Screens are not unit-tested; verify those on a device.

pnpm test        # finance calculations and data-integrity regressions
pnpm typecheck   # every package

Deploying a mini app

Build the container and serve its manifest and bundle from any static origin.

pnpm --filter @jinnie/hello bundle:android

Point the remote host at that origin. Because the host resolves remotes at runtime, publishing a new bundle updates the app for existing installs without an app-store release.

Questions about the SDK go to hello@jinnie.ph.