Skip to content

ntilau/iGitEra

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

iGitEra

iGitEra

A portable Gitea Git server that runs on your Android device

License: MIT Gitea Platform Go API

Start the server and you have a full-featured Git web interface accessible from any browser at http://<device-ip>:3000 — no cloud, no setup, just a portable Git server in your pocket.

Getting StartedArchitectureBuildingFAQContributing


Features

  • Full Gitea v1.22.6 — Issues, Pull Requests, repositories, wiki, Code, actions, and all Gitea features
  • Runs via Foreground Service — Server stays alive even when the app is in the background
  • In-app WebView — Access Gitea's UI directly within the app, no browser needed
  • Network-accessible — Any device on your LAN can reach the server at http://<device-ip>:3000
  • Port configuration — Choose any port before starting the server
  • Dump & Restore — Full data backup/restore via Android's Storage Access Framework (SAF)
  • SQLite storage — Zero external database dependencies; everything runs on-device
  • Offline mode — Fully air-gapped; no internet connection required

Getting Started

Quick Install

  1. Download the latest app-debug.apk from the Releases page
  2. Sideload it on your Android device (enable Install from unknown sources)
  3. Open the iGitEra app
  4. Tap Start Server — the Gitea server starts as a foreground service
  5. Tap Open Browser to access Gitea through the in-app browser
  6. Access from any device on your network at http://<device-ip>:3000

First-time user? The default credentials for new Gitea installations are created on first register — anyone can register since registration is open by default. You can lock this down in Gitea's admin panel.

What's Happening Under the Hood

When you tap Start Server:

  1. The app starts a Foreground Service to keep the process alive
  2. JNI loads libgiteawrapper.so (the Go bridge, ~2 MB)
  3. The Go bridge writes app.ini configuration to the app's data directory
  4. It fork/execs libgiteaserver.so (the Gitea binary, ~113 MB) as a subprocess
  5. Gitea starts listening on the configured port using SQLite3 storage
  6. A background goroutine periodically ensures all git repos are properly initialized

To stop, tap Stop Server — the Go bridge sends SIGTERM to the Gitea subprocess, waits up to 10 seconds, then force-kills if needed.

Screenshots

Main Screen WebView UI Server Running
(coming soon) (coming soon) (coming soon)

Architecture

┌──────────────────────────────────────────────────────────┐
│                    Android APK (com.igitera)              │
│                                                          │
│  ┌──────────────────┐          ┌──────────────────┐      │
│  │   MainActivity    │          │ WebViewActivity   │     │
│  │   (start/stop)    │─────────▶│ (in-app Gitea UI) │     │
│  └────────┬─────────┘          └──────────────────┘      │
│           │                                               │
│  ┌────────▼─────────┐                                    │
│  │   GiteaService    │  Foreground Service (dataSync)     │
│  │   (lifecycle)     │  Stay alive in background          │
│  └────────┬─────────┘                                    │
│           │ JNI (System.loadLibrary)                      │
│  ┌────────▼──────────────────┐                           │
│  │   libgiteawrapper.so      │  Go → C shared library    │
│  │   (subprocess manager)    │  ~2 MB                    │
│  │   ─ StartGiteaServer()    │  ZERO external deps       │
│  │   ─ StopGiteaServer()     │                           │
│  │   ─ IsServerRunning()     │                           │
│  └────────┬──────────────────┘                           │
│           │ fork/exec                                     │
│  ┌────────▼──────────────────┐                           │
│  │   libgiteaserver.so       │  Gitea v1.22.6 PIE binary │
│  │   (Gitea web server)      │  ~113 MB                  │
│  │   ─ gogit (pure-Go git)   │  jniLibs → native lib dir │
│  │   ─ sqlite3 (embedded DB) │  SELinux: apk_data_file ✓ │
│  │   ─ bindata (assets)      │                           │
│  └───────────────────────────┘                           │
│                                                          │
│  ┌───────────────────────────┐                           │
│  │   libgitshim.so           │  Git stub binary          │
│  │   (git version shim)      │  ~2 MB                    │
│  └───────────────────────────┘                           │
└──────────────────────────────────────────────────────────┘

Layer-by-Layer Breakdown

1. Android Layer (android/app/)

  • MainActivity.kt — Launcher activity with Start/Stop/Web UI controls, port input, and Dump/Restore buttons. Polls server status and updates UI accordingly.
  • GiteaService.kt — Android Foreground Service (dataSync type). Manages the server lifecycle with a persistent notification. Shows the server URL and a "Stop" action.
  • WebViewActivity.kt — Embedded Chromium WebView that loads the Gitea UI. Allows in-app zoom, intercepts external links, and handles back-navigation through browser history.
  • GiteaBridge.kt — Kotlin object declaring external fun JNI methods. Loads libgiteawrapper.so and exposes startServer(), stopServer(), isRunning().
  • BackupManager.kt — Creates and restores ZIP backups of the entire Gitea data directory via Android's Storage Access Framework.
  • NetworkUtils.kt — Detects the device's LAN IPv4 address, prioritizing WiFi/Ethernet over mobile data.

2. Go Wrapper (android/gitea-wrapper/)

  • main.go — Exports three C functions (StartGiteaServer, StopGiteaServer, IsServerRunning). Creates the data directory, writes app.ini, fork/execs the Gitea binary, monitors its lifetime, and runs a background goroutine that fixes uninitialized git repos.
  • settings.go — Generates Gitea's app.ini configuration on startup. Auto-detects LAN IP for correct clone URLs. Also contains ensureBareRepoInit() — a workaround for go-git's PlainInit silently failing on some Android filesystems.
  • jni_android.c — JNI C wrappers using standard Java_com_igitera_GiteaBridge_* naming convention (not RegisterNatives). Converts between JNI types and C types.
  • build-android.sh — All-in-one build script that cross-compiles all components for Android arm64-v8a.
  • gitea-patches.patch — Fixes a "concurrent map read and map write" panic in Gitea's modules/setting/setting.go by adding a sync.Mutex around the configuredPaths map.

3. Gitea Binary (libgiteaserver.so)

A full Gitea v1.22.6 server cross-compiled with these build tags:

  • gogit — Pure-Go git implementation (go-git). No system git binary needed.
  • sqlite3 — Embedded database via CGO. Requires the NDK's aarch64-linux-android28-clang.
  • bindata — HTML templates, CSS, JS, and images are compiled into the binary. Requires building webpack assets first.

4. Git Shim (libgitshim.so)

A minimal statically-linked C binary (~2 MB) that handles exactly three commands:

  • git version → reports git version 2.45.0
  • git config → returns success (exit 0)
  • git rev-parse → returns success (exit 0)

With the gogit build tag, Gitea never calls real git for operations — but it does check git version at startup, and the shim satisfies that check.

Why This Architecture?

Decision Rationale
Subprocess architecture The Go wrapper fork/execs Gitea as a separate process instead of importing it as a library. This avoids Gitea's ever-changing internal API and keeps the JNI bridge at ~2 MB instead of ~95 MB.
jniLibs packaging Android .apk files are ZIP archives. Native .so files in jniLibs/ are extracted by the package manager to the native library directory, where SELinux policy (apk_data_file context) allows execve(). The app's private data directory (/data/data/com.igitera/) is mounted noexec.
gogit build tag Android ships without a git binary. The gogit tag makes Gitea use the pure-Go go-git library instead. Skip this tag and Gitea will look for a git binary and fail.
bindata build tag Embeds all frontend assets directly into the binary. Without this, Gitea reads templates, CSS, and JS from the filesystem — which requires them to be shipped separately and accessible at the right paths.
Git shim Even with gogit, Gitea calls git version on startup to verify a sane git is available, and calls git config and git rev-parse in some initialization paths. The shim returns just enough to pass these checks.
Foreground Service Android aggressively kills background processes to save battery. A Foreground Service with a persistent notification tells the OS "this is important, don't kill it." The dataSync foreground service type is used for API 34+ compatibility.
JNI naming convention Uses standard Java_com_igitera_GiteaBridge_* naming instead of RegisterNatives. Standard JNI naming is simpler and avoids calling-convention mismatches that arise because Go-exported C functions don't receive (JNIEnv*, jclass) parameters.

Project Structure

iGitEra/
├── README.md                             # This file
├── LICENSE                               # MIT License
├── CLAUDE.md                             # Claude Code instructions
├── CONTRIBUTING.md                       # Contribution guidelines
├── SECURITY.md                           # Security policy
├── docs/
│   ├── ARCHITECTURE.md                   # Deep architecture dive
│   ├── DEVELOPMENT.md                    # Development setup guide
│   ├── TROUBLESHOOTING.md                # Common issues & fixes
│   └── API.md                            # JNI bridge API reference
├── android/
│   ├── app/
│   │   ├── build.gradle.kts              # Android app build config
│   │   ├── proguard-rules.pro            # ProGuard optimization rules
│   │   └── src/main/
│   │       ├── AndroidManifest.xml       # Permissions, activities, services
│   │       ├── kotlin/com/igitera/
│   │       │   ├── MainActivity.kt       # Launcher UI with start/stop controls
│   │       │   ├── GiteaService.kt       # Foreground service lifecycle
│   │       │   ├── GiteaBridge.kt        # JNI native method declarations
│   │       │   ├── WebViewActivity.kt    # In-app Gitea browser
│   │       │   ├── BackupManager.kt      # ZIP backup/restore
│   │       │   └── NetworkUtils.kt       # LAN IP detection
│   │       ├── jniLibs/arm64-v8a/
│   │       │   ├── libgiteawrapper.so    # JNI bridge (~2 MB)
│   │       │   ├── libgiteaserver.so     # Gitea binary (~113 MB)
│   │       │   └── libgitshim.so         # Git stub (~2 MB)
│   │       └── res/                      # Layouts, drawables, strings
│   ├── gitea-wrapper/
│   │   ├── main.go                       # C-exported Go entry points
│   │   ├── settings.go                   # Config generation + repo init fix
│   │   ├── jni_android.c                 # JNI bridge (C side)
│   │   ├── build-android.sh              # Cross-compile script
│   │   ├── go.mod                        # Zero external dependencies
│   │   ├── gitea-patches.patch           # Gitea concurrency fix
│   │   ├── gitea-src/                    # Gitea source (clone manually)
│   │   └── git-src/                      # Git source (clone manually)
│   ├── build.gradle.kts                  # Root Gradle build
│   ├── settings.gradle.kts               # Gradle module settings
│   ├── gradle.properties                 # JVM args, AndroidX
│   ├── release.keystore                  # Debug signing key
│   └── gradlew / gradlew.bat             # Gradle wrapper
└── .claude/
    └── settings.local.json               # Claude Code settings

Building

Full build instructions are in docs/DEVELOPMENT.md. The following is a quick summary.

Prerequisites

  • Go 1.22+ — for the Go wrapper and Gitea cross-compilation
  • Android NDK r25+ — for CGO cross-compilation to arm64-v8a
  • Android SDK 34 (compileSdk 35) — for the Kotlin app
  • Java 17+ — for Gradle
  • Node.js + npm — for building Gitea frontend assets (CSS/JS)

Quick Build (All-in-One)

cd android/gitea-wrapper

# Set your NDK path (or let the script auto-detect it)
export ANDROID_NDK_HOME=/path/to/android-ndk

# Build ALL native components + APK
./build-android.sh
cd ..
./gradlew assembleDebug

# Install on connected device
adb install app/build/outputs/apk/debug/app-debug.apk

Building Individual Components

See docs/DEVELOPMENT.md for detailed instructions on building each component separately:

  1. Build the Gitea binary
  2. Build the Git shim
  3. Build the Go wrapper .so
  4. Build the APK

Debugging

Reading Logs

Gitea server logs are stored in the app's private data directory:

# Gitea wrapper logs (Go bridge)
adb shell run-as com.igitera cat files/gitea-data/gitea.log

# Gitea subprocess output (stdout + stderr)
adb shell run-as com.igitera cat files/gitea-data/gitea-output.log

# Android logcat (app-level logging)
adb logcat -s GiteaService GiteaBridge

Server States

State What It Means Action
Running on port X Gitea is up and accepting connections Open browser or connect from another device
Starting... The Go wrapper is initializing and fork/execing Gitea Wait a few seconds
Stopped Server is not running Tap Start to begin
Failed to start Gitea exited shortly after launch Check gitea.log and gitea-output.log

Dump & Restore

iGitEra includes full data backup and restore:

  • Dump Data — Stops the server (if running), opens the Android file-saver dialog, and writes a ZIP of the entire gitea-data directory. After completion, the server restarts automatically if it was running.
  • Restore Data — Stops the server (if running), opens the file-picker for a ZIP file, wipes the current data directory, and extracts the backup. The server stays stopped after restore — tap Start to resume.

The backup ZIP contains all repos, the SQLite database, LFS objects, and uploaded attachments.

FAQ

Does this require root?

No. The app works on any standard Android device (API 28+/Android 9+). The native libraries are extracted by the package manager to a location where SELinux allows execution.

Does it need internet?

No. The server runs fully offline. Network access is only required for other devices on your LAN to connect.

How is this different from running Gitea in Termux?

Termux runs Gitea in a Linux container on Android. This project bundles Gitea as a compiled binary directly — no container, no Termux, no extra setup. It's a single APK install.

Can I access repos from my laptop?

Yes. Any device on the same network can clone/push/pull at http://<phone-ip>:3000/<username>/<repo>.git. The Go wrapper auto-detects the LAN IP and writes it into ROOT_URL.

Which architectures are supported?

Currently arm64-v8a only (most modern Android phones and tablets). ARM32 (armeabi-v7a) and x86_64 could be added with additional cross-compilation toolchains.

Can I change the port?

Yes. Enter a port number in the Port field before starting the server. The default is 3000. After the server starts, the port field is locked until you stop it.

Is SSH supported?

No. SSH is disabled in the generated configuration. The app focuses on HTTP(S) access only.

How do I update Gitea?

The Gitea binary is compiled from source at gitea-src/. To update, switch the git tag to a newer Gitea release and rebuild. Note that the API/behavior may change — see docs/DEVELOPMENT.md.

The app says "Failed to start" — what's wrong?

Check the logs (see Debugging). Common causes:

  • The port is already in use
  • The Gitea binary is corrupted or missing
  • SELinux is blocking execution (unlikely on stock Android, common on custom ROMs)

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

Security

If you discover a security vulnerability, please see SECURITY.md for reporting instructions.

License

MIT © iGitEra

About

iGitEra, i.e. "The Baboon", an Embedded Git Server! Android implementation based on Gitea

Topics

Resources

License

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors