Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8312b51
create flutter app shell
o-mid Jun 29, 2026
485646e
add dio client and auth api
o-mid Jun 30, 2026
f9df475
store tokens in secure storage
o-mid Jul 1, 2026
fed9bc9
add login and register screens
o-mid Jul 2, 2026
cb647b2
gate routes behind auth
o-mid Jul 3, 2026
1528f48
test auth cubit with fake client
o-mid Jul 3, 2026
90e9c88
add intent composer screen
o-mid Jul 6, 2026
0b96c6e
show plan steps for review
o-mid Jul 7, 2026
c9b7bb5
wire step approve and reject
o-mid Jul 8, 2026
c041aef
add balances and history screens
o-mid Jul 9, 2026
3da72ab
test plan review states
o-mid Jul 10, 2026
20c71a7
polish empty and error copy
o-mid Jul 10, 2026
5c72af8
add llm planner client behind interface
o-mid Jul 11, 2026
cb676d6
default mock planner in ci
o-mid Jul 12, 2026
007d0ef
timeout and schema fail handling
o-mid Jul 13, 2026
a89946e
test llm planner with fake transport
o-mid Jul 14, 2026
e7846b1
docs: planner mode env vars
o-mid Jul 14, 2026
85bf01e
add eval fixtures for plan accept reject
o-mid Jul 15, 2026
12e12f2
add github actions ci
o-mid Jul 15, 2026
303e060
docs: architecture and threat notes
o-mid Jul 16, 2026
d7de6d4
docs: demo script
o-mid Jul 16, 2026
d0d8679
polish root readme
o-mid Jul 17, 2026
7fb6010
chore: tidy api modules for evals
o-mid Jul 17, 2026
885b790
Merge pull request #11 from o-mid/feat/flutter-auth-shell
o-mid Jul 17, 2026
a6bde20
Merge pull request #12 from o-mid/feat/flutter-plan-ui
o-mid Jul 17, 2026
b92b5a4
Merge pull request #13 from o-mid/feat/llm-planner
o-mid Jul 17, 2026
acac16d
Merge pull request #14 from o-mid/chore/ci-evals-docs
o-mid Jul 17, 2026
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
55 changes: 55 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: ci

on:
push:
branches: [develop, main]
pull_request:

jobs:
contracts:
runs-on: ubuntu-latest
defaults:
run:
working-directory: contracts
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: foundry-rs/foundry-toolchain@v1
- run: forge test -vv

api:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: services/api/go.mod
cache-dependency-path: |
services/api/go.sum
packages/plan-schema/go.sum
- name: plan-schema tests
working-directory: packages/plan-schema
run: go test ./...
- name: api tests
working-directory: services/api
run: go test ./...
- name: evals
working-directory: services/api
run: go run ./cmd/evals -dir ../../evals/cases

mobile:
runs-on: ubuntu-latest
defaults:
run:
working-directory: apps/mobile
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version: "3.38.6"
channel: stable
cache: true
- run: flutter pub get
- run: flutter analyze --no-fatal-infos
- run: flutter test
93 changes: 90 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,92 @@
IntentGuard turns a natural-language DeFi intent into a schema-validated multi-step plan, requires per-step human approval, then executes against Foundry mocks on Anvil.
# IntentGuard

Layout: `apps/mobile` (Flutter), `services/api` (Go), `contracts` (Foundry), `packages/plan-schema`, `evals`, `deploy`, `docs`.
Natural-language DeFi intent → schema-validated multi-step plan → per-step human approval → execute against Foundry mocks on Anvil. The LLM (or mock planner) is untrusted; nothing hits the chain without schema, policy, and a human approve.

See `services/api/README.md` and `contracts/README.md` to run locally.
## Layout

```text
apps/mobile/ Flutter client (Bloc, Dio, go_router)
services/api/ Go API (auth, intents, planner, policy, executor)
packages/plan-schema/ Plan JSON Schema v1 + Go validate
contracts/ MockERC20 + MockSwapRouter (Foundry)
evals/ Accept/reject fixtures
deploy/ Docker Compose (API + Postgres + Anvil)
docs/ Architecture, threat model, demo script
```

## Architecture

```mermaid
flowchart LR
user[User]
flutter[FlutterApp]
api[API]
planner[Planner_mock_or_llm]
pg[(Postgres)]
anvil[Anvil]
mocks[MockContracts]

user --> flutter
flutter -->|JWT| api
api --> pg
api --> planner
api --> anvil
anvil --> mocks
```

Details: [`docs/architecture.md`](docs/architecture.md). Threat notes: [`docs/threat-model.md`](docs/threat-model.md).

## Run locally

```bash
cd deploy && docker compose up --build
```

In another shell (Foundry on the host):

```bash
./scripts/deploy-anvil.sh
./scripts/seed-anvil.sh
```

API `:8080`, Anvil `:8545`. Full curl path: [`docs/demo-script.md`](docs/demo-script.md).

Mobile:

```bash
cd apps/mobile
flutter run --dart-define=API_BASE=http://127.0.0.1:8080
```

## Env (API)

| Var | Default / notes |
|-----|-----------------|
| `DATABASE_URL` | required |
| `JWT_SECRET` | required |
| `PLANNER_MODE` | `mock` (CI/compose); `llm` needs `LLM_API_KEY` |
| `LLM_BASE_URL` | `https://api.openai.com/v1` |
| `LLM_MODEL` | `gpt-4o-mini` |
| `CHAIN_RPC_URL` | `http://127.0.0.1:8545` |
| `EXECUTOR_PRIVATE_KEY` | Anvil account #0 — local demo only |
| `DEPLOYMENTS_PATH` | `contracts/deployments/anvil.json` |

See [`services/api/README.md`](services/api/README.md).

## Tests / CI

```bash
cd contracts && forge test
cd services/api && go test ./...
cd services/api && go run ./cmd/evals -dir ../../evals/cases
cd apps/mobile && flutter test
```

GitHub Actions runs the same gates on PRs (`.github/workflows/ci.yml`).

## Design notes

- Plan actions v1: `approve`, `swap`, `transfer` only.
- Policy: spend cap, token/spender/recipient allowlists, max steps, slippage bound.
- Executor ABI-encodes stored step JSON — never raw model hex.
- Mock planner fixtures cover accept + schema/policy reject paths for demos and evals.
45 changes: 45 additions & 0 deletions apps/mobile/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
/coverage/

# Symbolication related
app.*.symbols

# Obfuscation related
app.*.map.json

# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
Empty file removed apps/mobile/.gitkeep
Empty file.
33 changes: 33 additions & 0 deletions apps/mobile/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: "8b872868494e429d94fa06dca855c306438b22c0"
channel: "[user-branch]"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 8b872868494e429d94fa06dca855c306438b22c0
base_revision: 8b872868494e429d94fa06dca855c306438b22c0
- platform: android
create_revision: 8b872868494e429d94fa06dca855c306438b22c0
base_revision: 8b872868494e429d94fa06dca855c306438b22c0
- platform: ios
create_revision: 8b872868494e429d94fa06dca855c306438b22c0
base_revision: 8b872868494e429d94fa06dca855c306438b22c0

# User provided section

# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
10 changes: 10 additions & 0 deletions apps/mobile/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# IntentGuard mobile

Flutter client for IntentGuard.

```bash
flutter pub get
flutter run --dart-define=API_BASE=http://127.0.0.1:8080
# Android emulator:
flutter run --dart-define=API_BASE=http://10.0.2.2:8080
```
28 changes: 28 additions & 0 deletions apps/mobile/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.

# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml

linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
14 changes: 14 additions & 0 deletions apps/mobile/android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
.cxx/

# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks
44 changes: 44 additions & 0 deletions apps/mobile/android/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
plugins {
id("com.android.application")
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
}

android {
namespace = "com.omid.intentguard.intentguard"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}

defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.omid.intentguard.intentguard"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}

buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
}
}
}

flutter {
source = "../.."
}
7 changes: 7 additions & 0 deletions apps/mobile/android/app/src/debug/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
45 changes: 45 additions & 0 deletions apps/mobile/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="intentguard"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.

In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.omid.intentguard.intentguard

import io.flutter.embedding.android.FlutterActivity

class MainActivity : FlutterActivity()
Loading
Loading