Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .bundle/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
BUNDLE_PATH: "vendor/bundle"
BUNDLE_FORCE_RUBY_PLATFORM: 1
4 changes: 4 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
root: true,
extends: '@react-native',
};
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-java@3a4f6e1af504cf6a318a3e152e9c3ef61916e9ce # v4.7.0
- uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
with:
java-version: '17'
distribution: 'temurin'
Expand Down
190 changes: 61 additions & 129 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,143 +1,75 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
# OSX
#
.DS_Store

# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
**/.xcode.env.local

# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
*.iml
*.hprof
.cxx/
*.keystore
!debug.keystore
.kotlin/

# node.js
#
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Optional REPL history
.node_repl_history
npm-debug.log
yarn-error.log

# Output of 'npm pack'
*.tgz
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/

# Yarn Integrity file
.yarn-integrity
**/fastlane/report.xml
**/fastlane/Preview.html
**/fastlane/screenshots
**/fastlane/test_output

# dotenv environment variable files
.env
.env.*
!.env.example
# Bundle artifact
*.jsbundle

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Ruby / CocoaPods
**/Pods/
/vendor/bundle/

# Next.js build output
.next
out
# Temporary files created by Metro to check the health of the file watcher
.metro-health-check*

# Nuxt.js build / generate output
.nuxt
dist
.output
# testing
/coverage

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp directory
.temp

# Sveltekit cache directory
.svelte-kit/

# vitepress build output
**/.vitepress/dist

# vitepress cache directory
**/.vitepress/cache

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# Firebase cache directory
.firebase/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# pnpm
.pnpm-store

# yarn v3
.pnp.*
# Yarn
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

# Vite files
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
.vite/
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "vendor/core"]
path = vendor/core
url = https://github.com/rustok-org/core.git
5 changes: 5 additions & 0 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
arrowParens: 'avoid',
singleQuote: true,
trailingComma: 'all',
};
1 change: 1 addition & 0 deletions .watchmanconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
109 changes: 58 additions & 51 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,86 +1,93 @@
# AGENTS.md — Rustok Mobile

> Overrides `meta/AGENTS.md` for `mobile/` subtree.
> Read `meta/AGENTS.md` first, then this file.
> Non-custodial device-signing wallet (Android-first). Key + approval live on
> the phone; the AI agent only *proposes*. Part of the `rustok-org/*` epic —
> see `core/.claude/specs/2026-06-25-epic-noncustodial-device-signing-PLAN.md`.

---

## Stack

- **Framework:** React Native 0.76+ (New Architecture)
- **Language:** TypeScript 5.6+, strict mode
- **Framework:** React Native 0.85.2 (New Architecture, Hermes)
- **Language:** TypeScript 5.8+, strict
- **Navigation:** React Navigation v7 (native-stack + bottom-tabs)
- **State:** Zustand 5 + MMKV persistence
- **Styling:** NativeWind 4 + TailwindCSS 3
- **Standards:** `~/Workspace/Codex/standards/react.md` + `typescript.md`
- **State:** Zustand 5 (+ MMKV for UI prefs)
- **Styling:** NativeWind 4 + TailwindCSS 3 (`global.css` design tokens)
- **Crypto bridge:** `react-native-rustok-bridge` (uniffi-bindgen-react-native)
over the org crate `vendor/core/crates/mobile-bindings` (git submodule).

---

## Architecture
## Design canon — the "mission control console"

### Chat-first UI
Source of truth: `core/.claude/specs/2026-06-29-stage2-wallet-design.md` (+ the
Claude Designer export). The wallet is an **approval console**, not a chat:

Main screen is **conversation**, not dashboard. Wallet actions are tools that LLM invokes.
- **Пульт (console)** — telemetry strip + hero approval card + queue feed.
- **Clear-signing card** — decoded action + risk verdict (BLOCK/WARN/ALLOW) +
findings; "arm → sign" ritual; BLOCK = override-with-friction (not a wall).
- **Composer** — manual action **and** "proposal from agent" (paste / scan) feed
the SAME card. **Receive**, history, settings.
- **Pairing** (WalletConnect-style) + agent status — surfaces only; the wire
protocol is Stage 4.

🚧 **Firewall:** the **chat / agent-console** (conversation-as-primary) is
**Stage 4**, NOT this stage. Do not design or build a ChatTab/chatStore now. The
agent seam is already in the design (pairing + "proposal from agent" in the
composer) — nothing to add.

### Phase-based nav (target)

```
AppShell
├── RootNavigator (phase-based)
│ ├── loading → SplashScreen
│ ├── no_wallet → OnboardingNavigator
│ ├── locked → UnlockPinScreen
│ └── unlocked → TabsNavigator
│ ├── ChatTab (PRIMARY)
│ ├── WalletTab (secondary)
│ ├── ActivityTab
│ └── SettingsTab
RootNavigator (phase)
├── loading → Splash
├── no_wallet → Onboarding
├── locked → UnlockPin
└── unlocked → Tabs ( Пульт · История · Настройки )
```

### State Management
(Tab glyphs are a geometric set — see the design export.)

---

Consolidate to **4 stores max**:
1. `walletStore` — phase, balance, address
2. `chatStore` — messages, tool calls, LLM state
3. `uiStore` — theme, modals, toasts
4. `settingsStore` — auto-lock, biometric, network
## Crypto bridge contract

Use `useShallow` selectors. No Redux.
The org crate is **stateless + synchronous + sign/analyze only** (no balances,
broadcast, swaps, or history on-device — those are Stage 3/4 server/wire). FFI
surface (`vendor/core/crates/mobile-bindings/src/ffi.rs`):

### Crypto Bridge
- `generate_wallet(wordCount)` → `{ mnemonic, address }` (backup display)
- `FfiWallet.import_mnemonic(phrase, password)` → handle
- `.address()`, `.sign_message(password, msg)`
- `.analyze(to, calldata, value)` → decoded action + structured findings (F3:
the device re-derives on-device, never trusts a server preview)
- `.sign_eip1559_tx(password, req)` / `.sign_legacy_tx(password, req)` → raw
EIP-2718 bytes

- **All crypto in Rust** — no JS crypto libraries
- `react-native-rustok-bridge` (uniffi-generated)
- Lazy singleton `WalletHandle`
- Numeric types cross FFI as **decimal strings** (U256 wei)
Numeric types cross FFI as **decimal strings** (U256 / u128). The bridge's
generated TS is regenerated by `ubrn build android` against the submodule — it
is gitignored, not committed.

---

## Key Rules
## Key rules

- **Named exports only** — no `export default`
- **Props:** `interface`, optional with defaults in destructuring
- **Hooks:** only at top level, unconditionally
- **Effects:** only for external sync, not data transformation
- **Types:** `interface` for objects, `type` for unions, `satisfies` for configs
- **Errors:** `catch (error: unknown)` + `instanceof Error`
- **Async:** `async/await`, `Promise.all` for independent ops
- **Props:** `interface`, defaults in destructuring.
- **Hooks** at top level only; **effects** only for external sync.
- **Types:** `interface` for objects, `type` for unions, `satisfies` for configs.
- **Errors:** `catch (error: unknown)` + `instanceof Error`. No `any`.
- **Async:** `async/await`, `Promise.all` for independent ops.

---

## CI Gates
## CI gates

```bash
npm run typecheck # tsc --noEmit
npm run lint # eslint
npm test # jest
# Android build: ./gradlew assembleDebug
cd android && ./gradlew assembleDebug # APK
```

---

## Bridge Contract

When adding new Rust → JS exports:
1. Add to `crates/rustok-mobile-bindings/src/lib.rs`
2. Regenerate bridge: `uniffi-bindgen-react-native`
3. Add TypeScript types in `mobile/src/lib/walletHandle.ts`
4. Test on both iOS and Android simulators
CI is `workflow_dispatch` until the bridge proof-of-life (PR-D1) is green; then
restore push/pull_request triggers.
Loading
Loading