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
35 changes: 34 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.0.13] - 2026-07-26

### Added

- iPhone, iPad, and Android now have thin Capacitor gateway clients for an
already configured 1Helm host. The connection screen accepts an HTTPS server
address, username, and password, confirms the host's explicit mobile
compatibility contract, and never exposes first-run host setup.
- Android ships as a directly distributed, signed universal APK with a stable
application ID and permanent release certificate. Release builds fail
closed without external signing material and verify alignment, package and
version metadata, v2/v3 signatures, and native ABI coverage.
- iOS has an App Store archive/export path with iPhone and iPad support, stable
bundle and callback identifiers, a privacy manifest, and the required
microphone, speech, and encryption declarations.

### Security

- Mobile passwords are used only for the sign-in request and are never saved.
Sessions are kept in the device-only iOS Keychain or encrypted with an
Android Keystore-held key; Disconnect erases the session and server address.
- The packaged local web client connects only to HTTPS servers, disables
native WebView debugging and release logging, rejects cleartext traffic and
Android backups, and limits server CORS to the two packaged Capacitor origins.

### Changed

- Authenticated API calls, files, avatars, workspace photos, terminals, app
events, external links, and provider OAuth flows now resolve correctly
through a selected mobile server while bundled brand and resident character
assets remain local.

## [0.0.12] - 2026-07-26

### Fixed
Expand Down Expand Up @@ -348,7 +380,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
notarization, stapled tickets, Gatekeeper verification, persistent
Application Support, and isolated Apple container machines.

[Unreleased]: https://github.com/gitcommit90/1Helm/compare/v0.0.12...HEAD
[Unreleased]: https://github.com/gitcommit90/1Helm/compare/v0.0.13...HEAD
[0.0.13]: https://github.com/gitcommit90/1Helm/releases/tag/v0.0.13
[0.0.12]: https://github.com/gitcommit90/1Helm/releases/tag/v0.0.12
[0.0.11]: https://github.com/gitcommit90/1Helm/releases/tag/v0.0.11
[0.0.10]: https://github.com/gitcommit90/1Helm/releases/tag/v0.0.10
Expand Down
35 changes: 30 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
 · 
<a href="https://1helm.com/manual/install-linux"><strong>for Linux</strong></a>
&nbsp;·&nbsp;
<a href="https://github.com/gitcommit90/1Helm/releases/latest"><strong>for Android</strong></a>
&nbsp;·&nbsp;
<a href="https://1helm.com">The story</a>
&nbsp;·&nbsp;
<a href="https://1helm.com/manual">Ship's manual</a>
Expand All @@ -32,7 +34,7 @@
<code>Self-hosted</code>&nbsp;&nbsp;
<code>Model-agnostic</code>&nbsp;&nbsp;
<code>Signed + notarized</code>&nbsp;&nbsp;
<code>macOS · Windows · Linux</code>
<code>macOS · Windows · Linux · iOS · Android</code>
</p>

---
Expand Down Expand Up @@ -132,6 +134,26 @@ digest-verified, health-checked updater — see the
it works best on a dedicated machine: your crew works around the clock, and
your everyday computer takes naps.

### Connect from iPhone, iPad, or Android

The mobile apps are thin, native gateways to a 1Helm you already run. Install
and finish setup on a supported Mac, Windows, or Linux host first, give that
host an HTTPS address, then enter the address, username, and password in the
mobile app. The password is used only for sign-in and is never retained; the
resulting session is stored in the iOS Keychain or encrypted with a key held by
Android Keystore.

- iPhone and iPad use the App Store build under the stable bundle identifier
`com.gitcommit90.onehelm.mobile`.
- Android is distributed directly as `1Helm-<version>-universal.apk` on the
matching GitHub Release. Android may ask you to allow installs from the app
you used to open the file. Verify the release SHA-256 and install it; future
releases signed by the same permanent 1Helm certificate install in place.
- The native clients require HTTPS, do not contain or initialize the 1Helm
server, and do not retain host data or provider credentials beyond the
selected server address and secure session token. Use **Disconnect** in the
profile menu to erase both from the device.

## Ready on day one. Specialized by day one hundred.

<p align="center">
Expand Down Expand Up @@ -223,6 +245,8 @@ and an audit trail. A prompt saying “use this service” is not a connector.
- Host-owned updates: a signed native Mac updater plus an atomic,
digest-verified Linux system service with health-check rollback.
- Signed, Apple-notarized, stapled Apple Silicon DMG releases.
- Native iPhone/iPad and directly distributed Android gateway clients for an
already configured HTTPS 1Helm host.

### Platform truth

Expand All @@ -231,10 +255,11 @@ and an audit trail. A prompt saying “use this service” is not a connector.
| **Apple Silicon macOS 26** | Native desktop product and real isolated Linux computer per resident (Apple `container machine`, `home-mount=none`). |
| **Linux / CI** | Supported headless systemd host with one unprivileged LXC per resident (subordinate UID/GID mapping, exact ownership checks); CI may select an explicit test backend. |
| **Windows 11 x64** | Native desktop product with one private WSL 2 world per resident (Windows-drive mounts and interop disabled); the signed Setup executable ships with every release. |
| **iPhone and iPad** | App Store gateway to an already configured HTTPS 1Helm host; sessions live in the device-only iOS Keychain. |
| **Android 7+** | Directly distributed signed universal APK gateway; sessions are encrypted by a key held in Android Keystore. |

Not yet shipped: a native Linux desktop shell, mobile clients, a hosted
control plane, rich Photon attachment fidelity, or blind execution of
community skills.
Not yet shipped: a native Linux desktop shell, a hosted control plane, rich
Photon attachment fidelity, or blind execution of community skills.

## Run the source workspace

Expand All @@ -257,7 +282,7 @@ A fresh data directory opens first-run setup. The source runtime defaults to
| `PORT` | `8123` | HTTP/WebSocket control-plane port. |
| `CTRL_DATA_DIR` | `./data` | Databases, routing state, uploads, and narrow workspace mirrors. |
| `HELM_CHANNEL_COMPUTER_BACKEND` | `apple` on macOS, `lxc` on Linux, `wsl` on Windows | Host isolation backend; `native` and `mock` are explicit development/test overrides. |
| `HELM_CHANNEL_MACHINE_IMAGE` | `local/1helm-channel-machine:0.0.12` | Versioned channel-machine image contract. |
| `HELM_CHANNEL_MACHINE_IMAGE` | `local/1helm-channel-machine:0.0.13` | Versioned channel-machine image contract. |

### Agent-first JSON CLI

Expand Down
101 changes: 101 additions & 0 deletions android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore

# Built application files
*.apk
*.aar
*.ap_
*.aab

# Files for the ART/Dalvik VM
*.dex

# Java class files
*.class

# Generated files
bin/
gen/
out/
# Uncomment the following line in case you need and you don't have the release build type files in your app
# release/

# Gradle files
.gradle/
build/

# Local configuration file (sdk path, etc)
local.properties

# Proguard folder generated by Eclipse
proguard/

# Log Files
*.log

# Android Studio Navigation editor temp files
.navigation/

# Android Studio captures folder
captures/

# IntelliJ
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml

# Keystore files
*.jks
*.keystore
*.properties.local

# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/

# Google Services (e.g. APIs or Firebase)
# google-services.json

# Freeline
freeline.py
freeline/
freeline_project_description.json

# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md

# Version control
vcs.xml

# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/

# Android Profiling
*.hprof

# Cordova plugins for Capacitor
capacitor-cordova-android-plugins

# Copied web assets
app/src/main/assets/public

# Generated Config files
app/src/main/assets/capacitor.config.json
app/src/main/assets/capacitor.plugins.json
app/src/main/res/xml/config.xml
2 changes: 2 additions & 0 deletions android/app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/build/*
!/build/.npmkeep
84 changes: 84 additions & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import java.util.Properties

apply plugin: 'com.android.application'

def oneHelmVersion = new groovy.json.JsonSlurper().parseText(file('../../package.json').text).version as String
def oneHelmVersionParts = oneHelmVersion.tokenize('.').collect { it as int }
def oneHelmVersionCode = oneHelmVersionParts[0] * 1000000 + oneHelmVersionParts[1] * 1000 + oneHelmVersionParts[2]
def signingPropertiesPath = System.getenv('HELM_ANDROID_SIGNING_PROPERTIES')
def signingProperties = new Properties()
if (signingPropertiesPath) {
def signingPropertiesFile = file(signingPropertiesPath)
if (!signingPropertiesFile.isFile()) throw new GradleException('HELM_ANDROID_SIGNING_PROPERTIES does not name a readable file.')
signingPropertiesFile.withInputStream { signingProperties.load(it) }
}

android {
namespace = "com.gitcommit90.onehelm.mobile"
compileSdk = rootProject.ext.compileSdkVersion
defaultConfig {
applicationId "com.gitcommit90.onehelm.mobile"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode oneHelmVersionCode
versionName oneHelmVersion
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
// Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61
ignoreAssetsPattern = '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
}
}
signingConfigs {
release {
if (signingPropertiesPath) {
storeFile file(signingProperties.getProperty('storeFile'))
storePassword signingProperties.getProperty('storePassword')
keyAlias signingProperties.getProperty('keyAlias')
keyPassword signingProperties.getProperty('keyPassword')
enableV1Signing true
enableV2Signing true
enableV3Signing true
enableV4Signing true
}
}
}
buildTypes {
release {
if (!signingPropertiesPath) throw new GradleException('Release builds require HELM_ANDROID_SIGNING_PROPERTIES.')
signingConfig signingConfigs.release
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

repositories {
flatDir{
dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs'
}
}

dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"
implementation project(':capacitor-android')
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
implementation project(':capacitor-cordova-android-plugins')
}

apply from: 'capacitor.build.gradle'

try {
def servicesJSON = file('google-services.json')
if (servicesJSON.text) {
apply plugin: 'com.google.gms.google-services'
}
} catch(Exception e) {
logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work")
}
24 changes: 24 additions & 0 deletions android/app/capacitor.build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN

android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_21
targetCompatibility JavaVersion.VERSION_21
}
}

apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies {
implementation project(':aparajita-capacitor-secure-storage')
implementation project(':capacitor-app')
implementation project(':capacitor-browser')
implementation project(':capacitor-keyboard')
implementation project(':capacitor-splash-screen')
implementation project(':capacitor-status-bar')

}


if (hasProperty('postBuildExtras')) {
postBuildExtras()
}
21 changes: 21 additions & 0 deletions android/app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.getcapacitor.myapp;

import static org.junit.Assert.*;

import android.content.Context;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;

/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {

@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();

assertEquals("com.getcapacitor.app", appContext.getPackageName());
}
}
Loading
Loading