Skip to content

Commit 737c9c3

Browse files
authored
docs(readme): correct CI workflow list + add AGENTS.md (#22)
* docs(readme): correct CI workflow list to match actual files Replace the stale GitHub Workflows subsection. Drop the non-existent release.yml reference, drop the false "Checks code quality" claim, and accurately describe android-ci.yml and release-manual.yml. * docs: add AGENTS.md for agent-assisted development Operating context for AI agents: project overview, architecture, build/verify commands, Kotlin/Go conventions, and common footguns.
1 parent 0582b38 commit 737c9c3

2 files changed

Lines changed: 211 additions & 12 deletions

File tree

AGENTS.md

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
# AGENTS.md
2+
3+
Operating context for AI agents (opencode, Cursor, Claude Code, etc.)
4+
working on this repository. Read this file first; it covers what the
5+
project is, how to build/verify it, and the conventions to follow.
6+
7+
## What this repo is
8+
9+
`GooseRelayVPN-AndroidClient` is the Android client for the
10+
GooseRelayVPN project. It wraps the upstream Go core (in `internal/`,
11+
shared with the server) in an Android `VpnService` and exposes a
12+
Jetpack Compose UI for VPN lifecycle, profile management, logs, and
13+
settings.
14+
15+
Upstream Go core: <https://github.com/kianmhz/GooseRelayVPN>
16+
This client: <https://github.com/ArashAfkandeh/GooseRelayVPN-AndroidClient>
17+
18+
## Architecture (Android side)
19+
20+
```
21+
┌─────────────────────────────────────────────┐
22+
│ Android UI Layer (Compose) │
23+
│ Home │ Profiles │ Settings │ Logs │ Info │
24+
└─────────────────────────────────────────────┘
25+
26+
┌─────────────────────────────────────────────┐
27+
│ ViewModels & Repository Layer │
28+
│ Room │ DataStore │ Hilt DI │
29+
└─────────────────────────────────────────────┘
30+
31+
┌─────────────────────────────────────────────┐
32+
│ VPN Service & Go Core │
33+
│ tun2socks │ GooseRelay Core (Go mobile) │
34+
└─────────────────────────────────────────────┘
35+
36+
┌─────────────────────────────────────────────┐
37+
│ Network Layer │
38+
│ SOCKS5 → Google Apps Script → VPS exit │
39+
└─────────────────────────────────────────────┘
40+
```
41+
42+
- `android/` — Gradle project (`settings.gradle.kts`, `build.gradle.kts`, `app/`). Kotlin 2.1.0,
43+
AGP 8.13.0, `minSdk=21`, `targetSdk=36`, `compileSdk=36`, JVM 17.
44+
- `android/app/src/main/java/com/gooserelay/gooserelayvpn/` — Kotlin source:
45+
- `service/``GooseRelayVpnService` (the VpnService),
46+
`VpnTileService` (Quick Settings tile), `BootReceiver` (boot,
47+
currently a no-op).
48+
- `ui/` — Compose screens and ViewModels, split by feature
49+
(`home/`, `profiles/`, `settings/`, `logs/`, `info/`,
50+
`navigation/`).
51+
- `data/local/` — Room (`AppDatabase` v3, `ProfileEntity`,
52+
`ProfileDao`).
53+
- `data/repository/``ProfileRepository` (Hilt `@Singleton`).
54+
- `dns/` — Kotlin FakeDNS interceptor (separate from the Go-side
55+
FakeDNS in `mobile/tun/`).
56+
- `di/` — Hilt `AppModule`.
57+
- `util/``VpnManager` (singleton bridge between UI and Go
58+
core), `ConfigGenerator` (profile → JSON config),
59+
`GlobalSettingsStore` (DataStore-backed).
60+
- `mobile/` — Go mobile bridge, gomobile bind target:
61+
- `mobile/mobile.go``StartClient` / `StopClient` /
62+
`StartTun` / `StopTun` / `StartTunBridge` / `StopTunBridge`
63+
exported to Kotlin via `mobile.Mobile.*`.
64+
- `mobile/tun/` — FakeDNS proxy (`fakedns_proxy.go`),
65+
DNS mapper (`dns_mapper.go`), `tun_api.go` (exported
66+
`StartFakeDNSProxy` etc.).
67+
- `internal/` — upstream Go core (carrier, session, socks, config,
68+
etc.). **Treat as read-only when working on the Android client.**
69+
- `apps_script/` — Google Apps Script deployment that fronts the
70+
carrier traffic over Google infrastructure.
71+
72+
## How to build
73+
74+
```bash
75+
# 1. Build the Go mobile AAR (requires Go 1.25+, NDK installed).
76+
# On Windows: use build_go_mobile.bat; on Unix: build_go_mobile.sh.
77+
bash ./android/build_go_mobile.sh
78+
# Output: android/app/libs/gooserelayvpn.aar
79+
80+
# 2. Build the debug APK.
81+
cd android
82+
./gradlew :app:assembleDebug
83+
# Output: android/app/build/outputs/apk/debug/GooseRelayVPN.apk
84+
85+
# 3. Build the release AAB (requires signing config in local.properties).
86+
./gradlew :app:bundleRelease
87+
```
88+
89+
## How to verify
90+
91+
```bash
92+
cd android
93+
94+
# Unit tests (JVM, no emulator needed).
95+
./gradlew :app:testDebugUnitTest --stacktrace
96+
97+
# Lint.
98+
./gradlew :app:lintDebug
99+
100+
# Compile-only fast check.
101+
./gradlew :app:compileDebugKotlin --stacktrace
102+
103+
# Go-side vet + format.
104+
go vet ./mobile/...
105+
gofmt -l mobile/
106+
```
107+
108+
Instrumented tests (`./gradlew :app:connectedDebugAndroidTest`)
109+
require a connected emulator or device; CI does not run them.
110+
111+
## Conventions
112+
113+
### Kotlin
114+
115+
- Style: `kotlin.code.style=official` (see
116+
`android/gradle.properties`).
117+
- ViewModels: `@HiltViewModel` + constructor injection. See
118+
`ui/profiles/ProfilesViewModel.kt` as the canonical example.
119+
- Singletons: `object` declarations for cross-cutting state
120+
(`VpnManager`, `ConfigGenerator`, `GlobalSettingsStore`).
121+
- Error handling: `runCatching { ... }.onFailure { ... }` for
122+
non-fatal failures; `try { ... } catch (_: Exception) {}` only
123+
for true background noise (e.g. closing sockets during shutdown).
124+
- Coroutines: `CoroutineScope(SupervisorJob() + Dispatchers.X)`.
125+
Use `withContext(Dispatchers.IO)` for thread hops; avoid nested
126+
`launch` blocks inside a coroutine. Guard with `isActive` after
127+
suspending operations.
128+
- Logging: `android.util.Log` for system logcat; `VpnManager.appendLog`
129+
for user-visible log lines (shown in the Logs screen, with a
130+
2000-line ring buffer). **Never log credentials**
131+
`ProfileEntity.socksPass`, `tunnelKey`, `scriptKeysText` (and their
132+
JSON-serialized forms in `ConfigGenerator.exportProfileJson`) must
133+
not appear in logs.
134+
135+
### Go (in `mobile/`)
136+
137+
- `gofmt`-clean; `go vet ./mobile/...` returns no findings.
138+
- Exported functions PascalCase (gomobile convention). Logger uses
139+
`log.Printf("[prefix] ...")` with brackets, e.g. `[TUN-API]`,
140+
`[client]`, `[socks]`.
141+
- Mutex discipline: `mu` guards app state (`running`, `tunActive`,
142+
`tunBridgeRunning`, `cancelFn`, `socksLn`, `clientDone`). `engineMu`
143+
guards the tun2socks engine Start/Stop. Take them in the same
144+
order every time to avoid deadlocks.
145+
- Errors wrapped with `fmt.Errorf("...: %w", err)`.
146+
147+
### Build / CI
148+
149+
- Commit style: `type(scope): subject` (conventional commits).
150+
Examples from `git log`: `fix(android): ...`,
151+
`feat(android): ...`, `perf(android): ...`, `style(ui): ...`,
152+
`chore(mobile): ...`, `docs(readme): ...`, `ci(android): ...`.
153+
- Don't push or open PRs unless the operator instructed it.
154+
- Don't commit secrets. Signing keys live in
155+
`$ANDROID_KEYSTORE_PATH` env var or in CI secrets.
156+
157+
## Things that are easy to get wrong
158+
159+
- **Credentials in logs.** Before adding any
160+
`VpnManager.appendLog(...)` or `Log.d(...)` call that includes
161+
profile data, redact `socksUser`, `socksPass`, `tunnelKey`, and
162+
`scriptKeysText`. The exported JSON config object contains all
163+
four.
164+
- **`fallbackToDestructiveMigration`.** Don't add new Room schema
165+
versions without a real `Migration` entry in
166+
`ProfileMigrations.ALL` (see `data/local/ProfileMigrations.kt`).
167+
A missing migration will crash the app at launch for any user
168+
whose DB schema version is lower than the new one.
169+
- **`engine.Stop()` panics.** The `recover()` blocks in
170+
`mobile.go`'s `StopTun`/`StopTunBridge` are load-bearing — they
171+
mask a tun2socks panic on stop that previously SIGSEGV'd the app
172+
(commit `41c3eef`). Don't remove them.
173+
- **`network_security_config.xml` allows cleartext globally.**
174+
Tightening this is on the roadmap; don't add new `http://`
175+
fetches to production paths without revisiting this config.
176+
- **Go mobile rebuild.** After any `mobile/*.go` change, run
177+
`bash ./android/build_go_mobile.sh` *before* `./gradlew :app:assembleDebug`
178+
— the Gradle build loads `android/app/libs/gooserelayvpn.aar` as a
179+
file dependency and will silently test against a stale AAR if the
180+
AAR isn't refreshed.
181+
182+
## Running the plans in `plans/`
183+
184+
The `plans/` directory contains self-contained implementation plans.
185+
Each plan's filename is `NNN-short-slug.md`; `plans/README.md` is the
186+
index (priority order, dependencies, status). Read the full plan
187+
before starting, honor the STOP conditions, update the status row in
188+
`plans/README.md` when done. Don't improvise when reality doesn't
189+
match the plan — report back to the operator.

README.md

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -309,20 +309,30 @@ Output: `app/build/outputs/bundle/release/app-release.aab`
309309
### GitHub Workflows
310310

311311
**`.github/workflows/android-ci.yml`**
312-
- Runs on every commit
313-
- Builds debug APK
314-
- Runs unit tests
315-
- Checks code quality
316-
317-
**`.github/workflows/release.yml`**
318-
- Triggered on version tag (v*.*.*)
319-
- Builds signed release APK/AAB
320-
- Creates GitHub Release with artifacts
321-
- Requires signing secrets
312+
- Runs on every push to `main`/`master` and on every pull request
313+
- Builds the Go mobile AAR (`bash ./android/build_go_mobile.sh`)
314+
- Builds the debug APK (`./gradlew :app:assembleDebug`)
315+
- Runs unit tests (`./gradlew :app:testDebugUnitTest`) — added in
316+
the test-baseline work; if your branch is older, this step may not
317+
be present yet
318+
- Uploads the APK and AAR as workflow artifacts for download from the
319+
Actions tab
322320

323321
**`.github/workflows/release-manual.yml`**
324-
- Manual trigger for releases
325-
- Same as release.yml but on-demand
322+
- Manually triggered (`workflow_dispatch`) with a required
323+
`tag_name` input (e.g. `v1.2.3`) and optional `release_name` /
324+
`make_latest` inputs
325+
- Builds a signed, split-by-ABI release APK set plus a universal APK
326+
and a Go AAR; requires the `ANDROID_KEYSTORE_BASE64`,
327+
`ANDROID_KEYSTORE_PASSWORD`, `ANDROID_KEY_ALIAS`, and
328+
`ANDROID_KEY_PASSWORD` repo secrets
329+
- Creates a GitHub Release with `softprops/action-gh-release@v2`
330+
and uploads the AAR and all APKs as release assets
331+
332+
> There is **no** `release.yml` workflow. Tag-triggered releases are
333+
> performed by manually dispatching `release-manual.yml` with the
334+
> desired tag. If you need a tag-triggered workflow, add one as a
335+
> sibling to `release-manual.yml` and update this section.
326336
327337
### Secrets Required
328338

0 commit comments

Comments
 (0)