-
Notifications
You must be signed in to change notification settings - Fork 1
Keystores
vibe-coder-server can generate and store Android signing keystores per
package name. The UI lives under Settings → Keystores
(/settings/keystores) and writes 4 files per package into the persistent
volume ${VIBE_DATA_ROOT}/dev-tools/keystores/ (host) →
/home/vibe/keystores/ (container).
For an Android applicationId like com.siamakerlab.myapp, creation produces:
| File | Purpose |
|---|---|
com.siamakerlab.myapp.keystore |
Release signing key (PKCS12, RSA 4096, validity 100 years by default (v1.54.2)) |
com.siamakerlab.myapp-debug.keystore |
Debug signing key (same DN + password, separate file so debug-vs-release fingerprints differ) |
com.siamakerlab.myapp-keystore.properties |
Gradle signing config: storeFile / storePassword / keyAlias / keyPassword
|
com.siamakerlab.myapp-admob.properties |
(Optional) AdMob app ID + ad-unit IDs — written only if at least one ID is provided in the form |
Permissions are set to 600 (files) and 700 (directory) on creation.
Edit server.yml (or its env equivalent) once with the operator's standard
distinguished-name fields:
keystore:
defaults:
name: "Jangwook Lee" # CN
organization: "Sia Makerlab" # O
unit: "Mobile" # OU
country: "KR" # C (2 letters)
state: "Chungbuk" # ST
city: "Jecheon" # L
defaultPassword: "********" # plaintext — env override recommended
validityYears: 100Then every keystore form in the UI is pre-filled and only requires the package name (and optionally AdMob IDs).
The plaintext defaultPassword is convenient but exposes the key — for
production-style deployments set the value via the env var
VIBECODER_KEYSTORE_DEFAULT_PASSWORD (or compose secret file) and leave
the YAML field blank.
- Open
/settings/keystores. - Read the Existing keystores table to confirm what already exists.
- Use the Create keystore form:
- Type the Android
applicationId(must match^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$). - Password (pre-filled from defaults).
- Optionally expand "AdMob IDs" if you want the
-admob.propertiesfile too. - Submit. The 4 files appear immediately.
- Type the Android
-
Back up
${VIBE_DATA_ROOT}/dev-tools/keystores/to a separate disk or cloud storage. Losing the release.keystoremakes Play Store updates to thatapplicationIdpermanently impossible.
You no longer have to leave for Settings → Keystores to sign a project.
When a project has no matching keystore yet, its Builds page
(/projects/{id}/builds, also the Builds tab inside the project tabs view)
shows the "keystore not prepared" warning box with a collapsible
Create this project's keystore form right inside it:
- The package name is locked to the project's
applicationId(read-only). ThePOST /projects/{id}/keystorehandler ignores any submitted package name and usesproject.packageName, so the new keystore set is auto-linked by the usual package-name prefix match — no extra wiring step. - Only the password is required. The distinguished-name fields (CN / O / OU / C / ST / L) and validity are pre-filled.
- On success the page reloads back on the same build page and the Queue build button becomes enabled immediately.
In addition to server.yml keystore.defaults, the last form input is
remembered so the next create form starts from what you typed before:
- After every successful create, the DN meta (name / organization / unit /
country / state / city) and validity years are cached to
/home/vibe/keystores/.last-input.properties. - Passwords, key aliases, and any secret values are never written to this cache — only the non-secret distinguished-name metadata. The password is always entered fresh.
- The cache file is
chmod 600, is not a.keystorefile (so it never shows up in the keystore list), andeffectiveDefaults()overlays it on top of theserver.ymldefaults when rendering any create form.
In app/build.gradle.kts:
import java.io.FileInputStream
import java.util.Properties
val keystorePropertiesFile = file("/home/vibe/keystores/com.siamakerlab.myapp-keystore.properties")
val keystoreProperties = Properties().apply {
if (keystorePropertiesFile.exists()) {
FileInputStream(keystorePropertiesFile).use { load(it) }
}
}
android {
signingConfigs {
create("release") {
if (keystorePropertiesFile.exists()) {
storeFile = file(keystoreProperties["storeFile"]!!)
storePassword = keystoreProperties["storePassword"] as String
keyAlias = keystoreProperties["keyAlias"] as String
keyPassword = keystoreProperties["keyPassword"] as String
}
}
}
buildTypes {
release {
signingConfig = signingConfigs.getByName("release")
}
}
}If your Gradle build runs outside the vibe-coder-server container, mount the keystores volume into the build container (or copy the relevant files to a path Gradle can read).
If the form's "AdMob IDs" block was filled, -admob.properties contains:
admobAppId=ca-app-pub-XXXX~YYYY
appOpenAdUnitId=ca-app-pub-XXXX/YYYY
bannerAdUnitId=ca-app-pub-XXXX/YYYY
nativeAdUnitId=ca-app-pub-XXXX/YYYYLoad it the same way as keystore.properties and apply via
manifestPlaceholders["admobAppId"] + buildConfigField("String", "BANNER_AD_UNIT_ID", "\"${...}\"").
The table next to each keystore has a Delete button. It removes all four files atomically and asks for confirmation. There is no recovery — make sure the host backup is current before deleting anything signed by a release key in production.
A future patch may expose GET /api/keystores + POST /api/keystores so
the Android client can manage keystores from the phone. For now the admin
UI is the only entry point.