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
61 changes: 48 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,19 @@ on:
workflow_dispatch:
inputs:
gomtm_swarm_aar_url:
description: Optional direct download URL for a gomtm swarm AAR
description: GitHub API asset URL for gomtm-swarm-android.aar
required: false
type: string
gomtm_swarm_metadata_url:
description: GitHub API asset URL for gomtm-swarm-android.json
required: false
type: string
gomtm_swarm_aar_version:
description: Version string for the pinned gomtm swarm AAR
required: false
type: string
gomtm_swarm_aar_sha256:
description: SHA256 for the pinned gomtm swarm AAR
required: false
type: string

Expand All @@ -14,7 +26,12 @@ permissions:

env:
GOMTM_SWARM_AAR_URL: ${{ inputs.gomtm_swarm_aar_url || vars.GOMTM_SWARM_AAR_URL }}
GOMTM_SWARM_AAR_NAME: ${{ vars.GOMTM_SWARM_AAR_NAME || 'gomtm-swarm-android.aar' }}
GOMTM_SWARM_METADATA_URL: ${{ inputs.gomtm_swarm_metadata_url || vars.GOMTM_SWARM_METADATA_URL }}
GOMTM_SWARM_AAR_VERSION: ${{ inputs.gomtm_swarm_aar_version || vars.GOMTM_SWARM_AAR_VERSION }}
GOMTM_SWARM_AAR_SHA256: ${{ inputs.gomtm_swarm_aar_sha256 || vars.GOMTM_SWARM_AAR_SHA256 }}
GOMTM_SWARM_AAR_NAME: gomtm-swarm-android.aar
GH_FALLBACK_TOKEN: ${{ github.token }}
GOMTM_RELEASE_TOKEN: ${{ secrets.GOMTM_GH_PAT }}
Comment on lines 27 to +34

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Keep the cross-repo token off the PR build steps.

GOMTM_RELEASE_TOKEN is defined at workflow scope, so every later step — including the Gradle commands that run pull-request code — inherits it. That makes the cross-repo credential exfiltrable from a same-repo PR. Scope both tokens to the download step only.

Suggested fix
 env:
   GOMTM_SWARM_AAR_URL: ${{ inputs.gomtm_swarm_aar_url || vars.GOMTM_SWARM_AAR_URL }}
   GOMTM_SWARM_METADATA_URL: ${{ inputs.gomtm_swarm_metadata_url || vars.GOMTM_SWARM_METADATA_URL }}
   GOMTM_SWARM_AAR_VERSION: ${{ inputs.gomtm_swarm_aar_version || vars.GOMTM_SWARM_AAR_VERSION }}
   GOMTM_SWARM_AAR_SHA256: ${{ inputs.gomtm_swarm_aar_sha256 || vars.GOMTM_SWARM_AAR_SHA256 }}
   GOMTM_SWARM_AAR_NAME: gomtm-swarm-android.aar
-  GH_FALLBACK_TOKEN: ${{ github.token }}
-  GOMTM_RELEASE_TOKEN: ${{ secrets.GOMTM_GH_PAT }}

       - name: Download pinned gomtm swarm AAR and metadata
+        env:
+          GH_FALLBACK_TOKEN: ${{ github.token }}
+          GOMTM_RELEASE_TOKEN: ${{ secrets.GOMTM_GH_PAT }}
         shell: bash
In GitHub Actions, do workflow-level env variables get injected into all later `run` and `uses` steps, and are repository secrets available to `pull_request` workflows for branches in the same repository?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 27 - 34, The workflow-level env
defines GOMTM_RELEASE_TOKEN (and GH_FALLBACK_TOKEN) which makes the cross-repo
secret available to all subsequent steps; remove these tokens from the top-level
env block and instead add them to the specific download step that fetches the
AAR (the "download" or AAR download step) by setting env: GOMTM_RELEASE_TOKEN:
${{ secrets.GOMTM_GH_PAT }} and GH_FALLBACK_TOKEN: ${{ github.token }} only on
that step so they are not injected into Gradle or other PR-run steps; update the
.github/workflows/ci.yml env usage to reference only per-step env for those
tokens and keep other variables (GOMTM_SWARM_*) at workflow scope.


jobs:
android:
Expand All @@ -24,19 +41,37 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Prepare optional gomtm swarm AAR
- name: Validate gomtm swarm AAR contract
shell: bash
run: |
test -n "${GOMTM_SWARM_AAR_URL}" || { echo "Missing GOMTM_SWARM_AAR_URL"; exit 1; }
test -n "${GOMTM_SWARM_METADATA_URL}" || { echo "Missing GOMTM_SWARM_METADATA_URL"; exit 1; }
test -n "${GOMTM_SWARM_AAR_VERSION}" || { echo "Missing GOMTM_SWARM_AAR_VERSION"; exit 1; }
test -n "${GOMTM_SWARM_AAR_SHA256}" || { echo "Missing GOMTM_SWARM_AAR_SHA256"; exit 1; }

- name: Download pinned gomtm swarm AAR and metadata
shell: bash
run: |
mkdir -p app/libs
if [ -z "${GOMTM_SWARM_AAR_URL}" ]; then
echo "No gomtm swarm AAR configured; building the public host shell without a bound runtime."
exit 0
fi

target="app/libs/${GOMTM_SWARM_AAR_NAME:-gomtm-swarm-android.aar}"
echo "Downloading optional gomtm swarm AAR to ${target}"
curl -fL --retry 3 --retry-all-errors "${GOMTM_SWARM_AAR_URL}" -o "${target}"
ls -lh app/libs
mkdir -p app/libs dist
auth_token="${GOMTM_RELEASE_TOKEN:-${GH_FALLBACK_TOKEN}}"
curl -fsSL -H "Authorization: Bearer ${auth_token}" -H "Accept: application/octet-stream" "${GOMTM_SWARM_AAR_URL}" -o "app/libs/${GOMTM_SWARM_AAR_NAME}"
curl -fsSL -H "Authorization: Bearer ${auth_token}" -H "Accept: application/octet-stream" "${GOMTM_SWARM_METADATA_URL}" -o "dist/gomtm-swarm-android.json"
echo "${GOMTM_SWARM_AAR_SHA256} app/libs/${GOMTM_SWARM_AAR_NAME}" | sha256sum --check --strict --status
python - <<'PY'
import json
import os
from pathlib import Path
meta = json.loads(Path('dist/gomtm-swarm-android.json').read_text())
expected_version = os.environ['GOMTM_SWARM_AAR_VERSION']
expected_sha = os.environ['GOMTM_SWARM_AAR_SHA256']
if meta.get('artifact') != 'gomtm-swarm-android.aar':
raise SystemExit(f"unexpected artifact: {meta.get('artifact')}")
if meta.get('version') != expected_version:
raise SystemExit(f"metadata version mismatch: {meta.get('version')} != {expected_version}")
if meta.get('sha256') != expected_sha:
raise SystemExit(f"metadata sha256 mismatch: {meta.get('sha256')} != {expected_sha}")
PY
ls -lh app/libs dist

- name: Set up Java 17
uses: actions/setup-java@v4
Expand Down
83 changes: 65 additions & 18 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,34 @@ on:
workflow_dispatch:
inputs:
gomtm_swarm_aar_url:
description: Optional direct download URL for a gomtm swarm AAR
description: GitHub API asset URL for gomtm-swarm-android.aar
required: false
type: string
gomtm_swarm_metadata_url:
description: GitHub API asset URL for gomtm-swarm-android.json
required: false
type: string
gomtm_swarm_aar_version:
description: Version string for the pinned gomtm swarm AAR
required: false
type: string
gomtm_swarm_aar_sha256:
description: SHA256 for the pinned gomtm swarm AAR
required: false
type: string

permissions:
contents: write

env:
RELEASE_TAG: v0.2.${{ github.run_number }}
RELEASE_TAG: v0.3.${{ github.run_number }}
GOMTM_SWARM_AAR_URL: ${{ inputs.gomtm_swarm_aar_url || vars.GOMTM_SWARM_AAR_URL }}
GOMTM_SWARM_AAR_NAME: ${{ vars.GOMTM_SWARM_AAR_NAME || 'gomtm-swarm-android.aar' }}
GOMTM_SWARM_METADATA_URL: ${{ inputs.gomtm_swarm_metadata_url || vars.GOMTM_SWARM_METADATA_URL }}
GOMTM_SWARM_AAR_VERSION: ${{ inputs.gomtm_swarm_aar_version || vars.GOMTM_SWARM_AAR_VERSION }}
GOMTM_SWARM_AAR_SHA256: ${{ inputs.gomtm_swarm_aar_sha256 || vars.GOMTM_SWARM_AAR_SHA256 }}
GOMTM_SWARM_AAR_NAME: gomtm-swarm-android.aar
GH_FALLBACK_TOKEN: ${{ github.token }}
GOMTM_RELEASE_TOKEN: ${{ secrets.GOMTM_GH_PAT }}
Comment on lines 29 to +37

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In GitHub Actions, if a job has contents: writeand a workflow-level env exposesgithub.token, can later build steps access a repo-write token via the environment?

💡 Result:

Yes, later build steps can access a repo-write token via the environment if the job has contents: write permission and the workflow-level env exposes github.token (e.g., env: GITHUB_TOKEN: ${{ github.token }}). The github.token context provides the GITHUB_TOKEN value, which inherits the job's permissions, including contents: write for repo writes like git push or API updates. Setting it in workflow-level env makes the variable available to all steps in all jobs. Steps can then use $GITHUB_TOKEN (or equivalent) for authenticated operations, such as git push or gh CLI commands. Official docs confirm github.token is equivalent to ${{ secrets.GITHUB_TOKEN }} and available in steps, with permissions controlled by the job's permissions block. Examples show passing it via env in steps for tools like gh, and the same applies at workflow level since env values propagate to steps.

Citations:


🏁 Script executed:

cat -n .github/workflows/release.yml

Repository: codeh007/gomtm-android

Length of output: 5964


Move repo-write tokens to the step that needs them.

GH_FALLBACK_TOKEN here carries contents: write, and both tokens are inherited by all steps—including assembleRelease, Java setup, and Android SDK configuration. Only the "Download pinned gomtm swarm AAR and metadata" step (line 55) actually uses them, so keep them step-local to follow least-privilege.

Suggested fix
 env:
   RELEASE_TAG: v0.3.${{ github.run_number }}
   GOMTM_SWARM_AAR_URL: ${{ inputs.gomtm_swarm_aar_url || vars.GOMTM_SWARM_AAR_URL }}
   GOMTM_SWARM_METADATA_URL: ${{ inputs.gomtm_swarm_metadata_url || vars.GOMTM_SWARM_METADATA_URL }}
   GOMTM_SWARM_AAR_VERSION: ${{ inputs.gomtm_swarm_aar_version || vars.GOMTM_SWARM_AAR_VERSION }}
   GOMTM_SWARM_AAR_SHA256: ${{ inputs.gomtm_swarm_aar_sha256 || vars.GOMTM_SWARM_AAR_SHA256 }}
   GOMTM_SWARM_AAR_NAME: gomtm-swarm-android.aar
-  GH_FALLBACK_TOKEN: ${{ github.token }}
-  GOMTM_RELEASE_TOKEN: ${{ secrets.GOMTM_GH_PAT }}

       - name: Download pinned gomtm swarm AAR and metadata
+        env:
+          GH_FALLBACK_TOKEN: ${{ github.token }}
+          GOMTM_RELEASE_TOKEN: ${{ secrets.GOMTM_GH_PAT }}
         shell: bash
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 29 - 37, The repository-wide env
block currently exposes GH_FALLBACK_TOKEN and GOMTM_RELEASE_TOKEN to every job
step; move these tokens out of the global env and into the env for only the
"Download pinned gomtm swarm AAR and metadata" step (the step that needs
GOMTM_SWARM_AAR_URL/metadata and the tokens) so other steps like
assembleRelease, Java setup, and Android SDK configuration don't inherit write
permissions; remove GH_FALLBACK_TOKEN and GOMTM_RELEASE_TOKEN from the top-level
env and add them as step-local env entries for that specific download step
(referencing GH_FALLBACK_TOKEN and GOMTM_RELEASE_TOKEN names and the step title
"Download pinned gomtm swarm AAR and metadata").


jobs:
release:
Expand All @@ -27,19 +44,46 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Prepare optional gomtm swarm AAR
- name: Validate gomtm swarm AAR contract
shell: bash
run: |
mkdir -p app/libs
if [ -z "${GOMTM_SWARM_AAR_URL}" ]; then
echo "No gomtm swarm AAR configured; releasing the public host shell without a bound runtime."
exit 0
fi
test -n "${GOMTM_SWARM_AAR_URL}" || { echo "Missing GOMTM_SWARM_AAR_URL"; exit 1; }
test -n "${GOMTM_SWARM_METADATA_URL}" || { echo "Missing GOMTM_SWARM_METADATA_URL"; exit 1; }
test -n "${GOMTM_SWARM_AAR_VERSION}" || { echo "Missing GOMTM_SWARM_AAR_VERSION"; exit 1; }
test -n "${GOMTM_SWARM_AAR_SHA256}" || { echo "Missing GOMTM_SWARM_AAR_SHA256"; exit 1; }

target="app/libs/${GOMTM_SWARM_AAR_NAME:-gomtm-swarm-android.aar}"
echo "Downloading optional gomtm swarm AAR to ${target}"
curl -fL --retry 3 --retry-all-errors "${GOMTM_SWARM_AAR_URL}" -o "${target}"
ls -lh app/libs
- name: Download pinned gomtm swarm AAR and metadata
shell: bash
run: |
mkdir -p app/libs dist
auth_token="${GOMTM_RELEASE_TOKEN:-${GH_FALLBACK_TOKEN}}"
curl -fsSL -H "Authorization: Bearer ${auth_token}" -H "Accept: application/octet-stream" "${GOMTM_SWARM_AAR_URL}" -o "app/libs/${GOMTM_SWARM_AAR_NAME}"
curl -fsSL -H "Authorization: Bearer ${auth_token}" -H "Accept: application/octet-stream" "${GOMTM_SWARM_METADATA_URL}" -o "dist/gomtm-swarm-android.json"
echo "${GOMTM_SWARM_AAR_SHA256} app/libs/${GOMTM_SWARM_AAR_NAME}" | sha256sum --check --strict --status
python - <<'PY'
import json
import os
from pathlib import Path
meta = json.loads(Path('dist/gomtm-swarm-android.json').read_text())
expected_version = os.environ['GOMTM_SWARM_AAR_VERSION']
expected_sha = os.environ['GOMTM_SWARM_AAR_SHA256']
if meta.get('artifact') != 'gomtm-swarm-android.aar':
raise SystemExit(f"unexpected artifact: {meta.get('artifact')}")
if meta.get('version') != expected_version:
raise SystemExit(f"metadata version mismatch: {meta.get('version')} != {expected_version}")
if meta.get('sha256') != expected_sha:
raise SystemExit(f"metadata sha256 mismatch: {meta.get('sha256')} != {expected_sha}")
provenance = {
'gomtm_swarm_aar_url': os.environ['GOMTM_SWARM_AAR_URL'],
'gomtm_swarm_aar_version': expected_version,
'gomtm_swarm_aar_sha256': expected_sha,
'gomtm_swarm_metadata_url': os.environ['GOMTM_SWARM_METADATA_URL'],
'release_tag': os.environ['RELEASE_TAG'],
'git_commit': os.environ['GITHUB_SHA'],
}
Path('dist/gomtm-android-provenance.json').write_text(json.dumps(provenance, indent=2) + '\n')
PY
ls -lh app/libs dist

- name: Set up Java 17
uses: actions/setup-java@v4
Expand Down Expand Up @@ -68,10 +112,13 @@ jobs:
tag_name: ${{ env.RELEASE_TAG }}
name: ${{ env.RELEASE_TAG }}
body: |
Public gomtm-android release.
gomtm-android release built from a pinned gomtm swarm AAR.

Integration mode:
- with configured gomtm swarm AAR: bound runtime host
- without configured gomtm swarm AAR: public host shell only
files: app/build/outputs/apk/release/app-release-unsigned.apk
gomtm AAR provenance:
- version: ${{ env.GOMTM_SWARM_AAR_VERSION }}
- url: ${{ env.GOMTM_SWARM_AAR_URL }}
- sha256: ${{ env.GOMTM_SWARM_AAR_SHA256 }}
files: |
app/build/outputs/apk/release/app-release-unsigned.apk
dist/gomtm-android-provenance.json
Comment on lines +121 to +123

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -C3 'assembleRelease|app-release-unsigned\.apk' .github/workflows/release.yml
build_file="$(fd 'build.gradle.kts$' app | head -n1)"
[ -n "$build_file" ]
rg -n -C5 'signingConfigs|buildTypes|release' "$build_file"

Repository: codeh007/gomtm-android

Length of output: 960


🏁 Script executed:

fd 'build.gradle.kts$' app | head -n1 | xargs cat -n

Repository: codeh007/gomtm-android

Length of output: 1847


Publish a signed APK here, not app-release-unsigned.apk.

app-release-unsigned.apk is the raw unsigned output from assembleRelease. The app/build.gradle.kts release buildType has no signingConfig defined, so the GitHub release currently uploads an artifact users cannot install with the Android package manager. Add a signing configuration to the release buildType and update the workflow to reference the signed output path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 117 - 119, The workflow uploads
the unsigned artifact app-release-unsigned.apk; fix by adding a signing
configuration in app module build.gradle.kts (create a signingConfigs block and
reference it from the release buildType so release.buildType uses signingConfig
release) and provide keystore credentials via secure properties/ENV (so the
Gradle release variant produces a signed APK), then update the GitHub Actions
workflow to publish the signed output (e.g., the release APK path produced by
the signed release variant such as
app/build/outputs/apk/release/app-release.apk) instead of
app-release-unsigned.apk. Ensure you reference the release buildType and
signingConfigs identifiers when making the changes.

fail_on_unmatched_files: true
46 changes: 19 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# gomtm-android

Public Android app host repository for gomtm.
Public Android swarm node app for gomtm.

## Current source of truth

Expand All @@ -9,54 +9,46 @@ This repository is the long-term public Android app host for gomtm.
The active architecture is:

- `gomtm` remains the swarm kernel / Android AAR producer
- `gomtm-android` is the public Android app host, CI, release, and APK distribution repo
- `gomtm-android` is the public Android node app host, CI, release, and APK distribution repo
- swarm-first comes before worker orchestration, group control, or reward automation
- legacy `apps/android/` in the monorepo is migration material only, not the future app host

## What is implemented in this repo today

This repo now ships a **swarm-first host shell** instead of a generic HelloWorld placeholder.

That means:

- the app can build and release publicly even when no gomtm swarm AAR is present
- the app contains a runtime probe screen that detects whether a gomtm bridge is actually bound
- the app uses reflection-based probing so it does not fake a compile-time dependency on an AAR that upstream does not yet publish stably
- GitHub Actions can optionally download a prebuilt gomtm swarm AAR into `app/libs/` before building
- legacy monorepo `apps/android/` is migration material only, not the future app host

## What this repo is responsible for

- Android UI and product shell
- public pull-request workflow
- starting and stopping a real gomtm swarm node runtime
- showing runtime state, peer id, bootstrap address, logs, and discovered peers
- GitHub Actions CI and release automation
- APK publication
- optional consumption of a gomtm-produced swarm AAR
- consuming a **published, pinned** `gomtm-swarm-android.aar`

## What this repo is not responsible for

- re-implementing libp2p or swarm logic in Kotlin
- reviving or continuing development in monorepo `apps/android/`
- pretending that a stable public gomtm AAR feed already exists
- pulling reward-automation business logic into the Android host shell
- consuming an unpinned `latest` AAR
- pulling reward-automation business logic into the Android node host

## Validation policy

- use GitHub Actions as the build / validation path
- use pull requests for functional changes
- do not treat local Android builds as the acceptance path for this phase

## Optional AAR consumption

If CI or a maintainer provides a gomtm swarm AAR, it should be placed into `app/libs/`.
## Runtime contract

The host app then probes for known bridge classes at runtime and reports:
The app consumes a published `gomtm-swarm-android.aar` and drives the current node runtime surface:

- whether a bridge was detected
- which bridge class was found
- whether the lifecycle surface looks like `node`, `worker-legacy`, or `probe-only`
- runtime state, peer id, bootstrap address, last error, and drained logs when exposed by the bridge
- `startNode(baseDir, config)`
- `stopNode()`
- `getState()`
- `getPeerID()`
- `getBootstrapAddr()`
- `getLastError()`
- `getDiscoveredPeers()`
- `drainLogs()`

If no AAR is present, the app still builds and clearly reports that it is running as a **public host shell only**.
The UI is expected to show real runtime data, not a host-shell placeholder.

## Related docs

Expand Down
7 changes: 4 additions & 3 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ android {
applicationId = "com.gomtm.android"
minSdk = libs.versions.minSdk.get().toInt()
targetSdk = libs.versions.targetSdk.get().toInt()
versionCode = 2
versionName = "0.2.0"
versionCode = 3
versionName = "0.3.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down Expand Up @@ -42,14 +42,15 @@ android {
}

dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar"))))
implementation(files("libs/gomtm-swarm-android.aar"))

implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.constraintlayout)

testImplementation(libs.junit)
testImplementation("org.json:json:20240303")
androidTestImplementation(libs.androidx.test.ext.junit)
androidTestImplementation(libs.androidx.espresso.core)
}
10 changes: 3 additions & 7 deletions app/libs/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
Place optional prebuilt gomtm swarm AAR artifacts here.
GitHub Actions downloads the pinned `gomtm-swarm-android.aar` into this directory before Gradle runs.

Expected direction:
- `gomtm` produces the Android swarm AAR
- `gomtm-android` consumes it from `app/libs/` during CI or release builds

This directory is intentionally kept in-repo so GitHub Actions can download the AAR here before Gradle runs.
Do not commit secrets or unrelated binaries.
This repo does not treat local or ad-hoc AAR files as the public source of truth.
The public truth is the published gomtm Android swarm AAR release asset plus its pinned SHA256.
3 changes: 3 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<application
android:allowBackup="true"
android:label="@string/app_name"
Expand Down
Loading
Loading