Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
4a3ebb1
docs: add native capture CPU path plan
Chinmay-KB Aug 31, 2026
bd2ee96
Record native CPU capture Phase 0 baseline
cursoragent Aug 31, 2026
53463ce
Define native CPU capture architecture and ADRs
cursoragent Aug 31, 2026
d64da61
Move Flutter packages into the mobile monorepo layout
cursoragent Aug 31, 2026
e6ee1b4
Add C++ image core with versioned C ABI
cursoragent Aug 31, 2026
f42d7a1
Add Android capture-runtime AAR and local Maven publish
cursoragent Aug 31, 2026
7bea55e
Add Flutter plugin bridge and experimental native CPU capture
cursoragent Aug 31, 2026
999296f
Add native CPU privacy contract tests and sign-off gate
cursoragent Aug 31, 2026
06b8e75
Record CPU capture measurement method and keep native opt-in
cursoragent Aug 31, 2026
758d467
Add monorepo docs and path-aware release controls
cursoragent Aug 31, 2026
ff8c3d5
Hold native CPU publication until device gates pass
cursoragent Aug 31, 2026
2a67ecf
Stop native captureMicros from double-counting nested stages
cursoragent Sep 1, 2026
d47aead
Fix iOS Pigeon method visibility
Chinmay-KB Sep 1, 2026
235cfc0
Unblock Flutter 3.47 release APKs and cover mask geometry
cursoragent Sep 1, 2026
e7cade4
Add experimental iOS native CPU capture runtime
cursoragent Sep 1, 2026
3878e33
Fix iOS Metal capture through the live Flutter layer
Chinmay-KB Sep 1, 2026
01081c7
Simplify Apple capture runtime after quality review
cursoragent Sep 1, 2026
814b12f
Emit requested and resolved capture backend on published frames
cursoragent Sep 1, 2026
1f86701
Emit overlay identity and presentation parent on route_change
cursoragent Sep 1, 2026
a020f29
Copy cause fingerprints at route_change emit time
cursoragent Sep 1, 2026
ac754fd
Separate public SDK docs from internal working notes
cursoragent Sep 1, 2026
2d541ed
Stop requiring unpublished native runtimes for Flutter consumers
cursoragent Sep 1, 2026
a7da9c7
Transfer PixelCopy bitmap ownership on timeout and interrupt
cursoragent Sep 1, 2026
630a92b
Align plugin packaging metadata and bind iOS capture to the engine view
cursoragent Sep 1, 2026
de5a20e
Merge main and retarget native capture onto 0.8.12
cursoragent Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
37 changes: 37 additions & 0 deletions .github/workflows/android-runtime.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Android runtime

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths:
- "platforms/android/**"
- "core/image-processing/**"
- "tool/ci/build-android-runtime.sh"
- "tool/ci/dump-android-runtime-api.py"
- "tool/ci/verify-android-runtime-api.sh"
- ".github/workflows/android-runtime.yml"

permissions:
contents: read

jobs:
aar:
name: Build and publish local AAR
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- name: Set up Android SDK
uses: android-actions/setup-android@v3
- name: Install NDK and CMake
run: |
yes | sdkmanager --install "ndk;28.2.13676358" "cmake;3.22.1" "platforms;android-36" "build-tools;35.0.0"
- name: Build runtime, tests, local Maven, sample
run: bash tool/ci/build-android-runtime.sh
- name: Verify public API dump
run: bash tool/ci/verify-android-runtime-api.sh
39 changes: 39 additions & 0 deletions .github/workflows/flutter-adapter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Flutter adapter

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]

permissions:
contents: read

jobs:
pigeon:
name: Verify Pigeon outputs
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
- name: Install Dart dependencies
run: flutter pub get
- name: Verify generated native capture bridges
run: bash tool/ci/verify-native-capture-pigeon.sh
Comment on lines +23 to +24

pub-dry-run:
name: pub publish dry-run
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
- name: Install Dart dependencies
run: dart pub get
- name: Dry-run pub publish
run: bash tool/ci/pub-dry-run.sh
44 changes: 44 additions & 0 deletions .github/workflows/image-core.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Image core

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
push:
paths:
- "core/image-processing/**"
- "tool/ci/run-image-core-tests.sh"
- ".github/workflows/image-core.yml"

permissions:
contents: read

jobs:
test:
name: Unit tests with ASan/UBSan
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configure, build, test
run: bash tool/ci/run-image-core-tests.sh

fuzz:
name: Short libFuzzer run
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install clang
run: sudo apt-get update && sudo apt-get install -y clang libclang-rt-dev
- name: Build fuzzers
run: |
cmake -S core/image-processing -B build/image-core-fuzz \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
-DTB_IMAGE_CORE_FUZZ=ON
cmake --build build/image-core-fuzz
- name: Fuzz masks
run: ./build/image-core-fuzz/tb_mask_fuzz -max_total_time=20 -timeout=5
- name: Fuzz metadata
run: ./build/image-core-fuzz/tb_metadata_fuzz -max_total_time=20 -timeout=5
81 changes: 27 additions & 54 deletions .github/workflows/version-bump-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,68 +9,41 @@ permissions:

jobs:
check-tugboat-version:
name: Require tugboat version bump
name: Require path-aware version policy
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Compare package version to base
- name: Compare versions to base
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
PACKAGE_PUBSPEC: packages/tugboat/pubspec.yaml
run: |
set -euo pipefail

extract_version() {
local file="$1"
if [[ ! -f "$file" ]]; then
echo ""
return
fi
grep -E '^version:' "$file" | head -1 | sed -E 's/^version:[[:space:]]*//' | tr -d '"' | tr -d "'" | tr -d '[:space:]'
}

if [[ ! -f "$PACKAGE_PUBSPEC" ]]; then
echo "::error::$PACKAGE_PUBSPEC is missing on this branch"
exit 1
fi

head_version="$(extract_version "$PACKAGE_PUBSPEC")"
if [[ -z "$head_version" ]]; then
echo "::error::Could not read version from $PACKAGE_PUBSPEC"
exit 1
fi

base_file="$(mktemp)"
if ! git show "${BASE_SHA}:${PACKAGE_PUBSPEC}" >"$base_file" 2>/dev/null; then
echo "Base branch has no $PACKAGE_PUBSPEC; treating as new package (ok)."
echo "PR version: $head_version"
exit 0
fi
run: bash tool/ci/check-version-policy.sh

base_version="$(extract_version "$base_file")"
rm -f "$base_file"

if [[ -z "$base_version" ]]; then
echo "::error::Could not read version from base $PACKAGE_PUBSPEC"
exit 1
fi

echo "Base version: $base_version"
echo "PR version: $head_version"

if [[ "$head_version" == "$base_version" ]]; then
echo "::error::packages/tugboat version must be bumped before merge (still $base_version)."
exit 1
fi
license:
name: AGPL-3.0-only license
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Check license files
run: bash tool/ci/check-license.sh

higher="$(printf '%s\n%s\n' "$base_version" "$head_version" | sort -V | tail -n 1)"
if [[ "$higher" != "$head_version" ]]; then
echo "::error::PR version ($head_version) must be greater than base ($base_version)."
exit 1
fi
android-api:
name: Android runtime API surface
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Diff public API dump
run: bash tool/ci/verify-android-runtime-api.sh

echo "Version bump detected: $base_version -> $head_version"
swift-api:
name: Swift API surface
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Diff public API dump
run: bash tool/ci/verify-swift-api.sh
Comment on lines +45 to +49
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,17 @@ coverage/
build/
**/build/
**/.flutter-plugins-dependencies
.local-maven/
platforms/android/local.properties
**/.gradle/
**/.gradle/**
.cxx/
**/.cxx/
**/.cxx/**
.build/
**/.build/
.swiftpm/
**/.swiftpm/
crash-*
leak-*
timeout-*
58 changes: 58 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// swift-tools-version: 5.9
import PackageDescription

let package = Package(
name: "TugboatCaptureRuntime",
platforms: [
.iOS(.v15),
],
products: [
.library(
name: "TugboatCaptureRuntime",
targets: ["TugboatCaptureRuntime"]
),
],
targets: [
.target(
name: "TugboatImageCore",
path: "core/image-processing",
exclude: [
"CMakeLists.txt",
"README.md",
"tests",
"fuzz",
],
sources: ["src/tb_image_core.cpp"],
publicHeadersPath: "include",
cxxSettings: [
.headerSearchPath("include"),
],
linkerSettings: [
.linkedLibrary("c++"),
]
),
.target(
name: "TugboatImageCoreBridge",
dependencies: ["TugboatImageCore"],
path: "platforms/apple/Sources/TugboatImageCoreBridge",
publicHeadersPath: ".",
cxxSettings: [
.headerSearchPath("../../../../core/image-processing/include"),
],
linkerSettings: [
.linkedLibrary("c++"),
]
),
.target(
name: "TugboatCaptureRuntime",
dependencies: ["TugboatImageCoreBridge"],
path: "platforms/apple/Sources/TugboatCaptureRuntime"
),
.testTarget(
name: "TugboatCaptureRuntimeTests",
dependencies: ["TugboatCaptureRuntime"],
path: "platforms/apple/Tests/TugboatCaptureRuntimeTests"
),
],
cxxLanguageStandard: .cxx17
)
78 changes: 56 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,50 @@
# Tugboat Flutter

Flutter SDK for capturing session evidence for Tugboat.
The Tugboat CLI is maintained separately; this repository contains only Flutter and
Dart packages.

**Repository:** [github.com/blendto/tugboat-flutter](https://github.com/blendto/tugboat-flutter)

## Packages

- [`tugboat`](packages/tugboat) — the Flutter SDK
- [`tugboat/example`](packages/tugboat/example) — demo app and integration fixture (not published)

## Documentation

See [docs/](docs/README.md) for integration guides and design notes.
# Tugboat mobile

Mobile capture SDKs for Tugboat. This repository is a mobile monorepo:
a portable C++ image core, native capture runtimes, and framework adapters.

The GitHub remote is still
[blendto/tugboat-flutter](https://github.com/blendto/tugboat-flutter) until an
administrator renames it to `tugboat-mobile`. See
[repository migration](docs/releases/repository-migration.md).

The Tugboat CLI is maintained separately.

## Layout

| Path | Product |
| --- | --- |
| `core/image-processing` | Portable C++ CPU core (C ABI) |
| `platforms/android` | `com.tugboat.sdk:capture-runtime` AAR |
| `platforms/apple` | Apple runtime (`TugboatCaptureRuntime` 0.1.0, unpublished) |
| `sdks/flutter/packages/tugboat` | Flutter adapter / plugin |
| `sdks/flutter/packages/tugboat_dio` | Dio network evidence |
| `sdks/flutter/packages/tugboat/example` | Demo app (not published) |
| `sdks/react-native` | Future adapter placeholder |
| `docs/` | Architecture, integration, privacy, performance, releases |
| `tool/ci` | Host test, generate, and release-control scripts |

Do not copy the C++ core into the pub package. Flutter consumes the Android
AAR and the Apple CocoaPod (local path during development) rather than
vendoring native sources.

## Capture backends

`package:tugboat` still defaults to Flutter `RepaintBoundary` screenshots.
Android native CPU capture is an experimental opt-in
(`TugboatScreenshotCaptureBackend.nativeCpuExperimental`). iOS uses the
same opt-in flag for a live Flutter-layer CPU path. Both stay experimental
until privacy device rows and
[native capture contracts](docs/architecture/native-capture-contracts.md) plus
the [experimental native CPU](docs/integration/native-cpu-experimental.md)
gates pass. Raw pixels never enter Dart. See
[experimental native CPU capture](docs/integration/native-cpu-experimental.md).

## Requirements

- Flutter 3.35.0 or newer
- Dart 3.9.2 or newer
- Android NDK `28.2.13676358` and CMake `3.22.1` to build the AAR
- Git

The repository uses a native Dart pub workspace for local package resolution and
Expand All @@ -31,9 +57,14 @@ From the repository root:
```sh
dart pub get
dart run melos list
dart run melos run analyze
bash tool/ci/run-image-core-tests.sh
bash tool/ci/build-android-runtime.sh
bash tool/ci/run-flutter-tests.sh
```

The Android AAR publishes to untracked `.local-maven/`. Run the Android build
before `flutter build apk` on the example.

## Development

```sh
Expand All @@ -48,12 +79,15 @@ Package-specific tests:
dart run melos run test:sdk
```

## Workspace conventions
Public SDK imports use `package:tugboat/tugboat.dart`. Keep package versions
and changelogs independent. Run dependency commands from the repository root;
the workspace has one shared `pubspec.lock`.

## Documentation

- Run dependency commands from the repository root. The workspace has one shared
`pubspec.lock`; package-level lockfiles should not be committed.
- Public SDK imports use `package:tugboat/tugboat.dart`.
- Keep package versions and changelogs independent.
See [docs/](docs/README.md) for public architecture, integration, privacy, and
release process. Working notes are not product docs; see
[docs/publishing.md](docs/publishing.md).

## License

Expand Down
Loading
Loading