security: rotate CVM identity and externalize signing
Some checks are pending
Deploy to GitHub Pages / deploy (push) Waiting to run
Some checks are pending
Deploy to GitHub Pages / deploy (push) Waiting to run
This commit is contained in:
parent
ab90e02f69
commit
3f28c3fdb4
6 changed files with 45 additions and 25 deletions
|
|
@ -2405,7 +2405,7 @@ The committed `package-lock.json` (from commit `f25fc2b`) had entries stripped o
|
|||
### Android release build
|
||||
- `npm run android:build` → `android/app/build/outputs/apk/release/app-release.apk`.
|
||||
- **Versioning**: `package.json` `version` is the single source of truth; `scripts/sync-android-version.mjs` syncs it to `versionName`/`versionCode` (e.g. `1.4.5` → `10405`) during the build. Bump **only** `package.json`.
|
||||
- **Signing**: secrets live in the gitignored `keystore.properties`; `scripts/inject-signing-config.mjs` injects a managed block into `android/app/build.gradle` (runs in `android:init`/`android:sync`/`android:build`). The authoritative keystore is **`/home/sovereignsk8/skatespots-release.jks`** (alias `skatespots`). An older repo-root copy had a wrong password and was retired to `~/.backups/keystore/`.
|
||||
- **Signing**: secrets live **outside the repo** at `~/.config/skatespots/android/keystore.properties`; `scripts/inject-signing-config.mjs` injects a managed block into `android/app/build.gradle` (runs in `android:init`/`android:sync`/`android:build`). The authoritative keystore is **`~/.config/skatespots/android/skatespots-release.jks`** (alias `skatespots`, mode 600). An older repo-root copy had a wrong password and was retired to `~/.backups/keystore/`. Never put signing material back in the repo: Vite serves files by filesystem path, not by `.gitignore`.
|
||||
- `android/` is **generated and gitignored**; recreate with `npm run android:init`.
|
||||
- **Launch splash:** `resources/splash.png` is the orange brand wheel. Capacitor's
|
||||
generated drawables used a stock blue placeholder, so `scripts/make-android-splash.mjs`
|
||||
|
|
@ -3431,7 +3431,7 @@ The community relay moved off the hosted relay.tools instance
|
|||
- `git push` may print a benign `cannot lock ref ... incorrect old value provided` retry warning; verify with `git fetch origin && git rev-list --left-right --count origin/main...HEAD`.
|
||||
|
||||
### Secrets — never commit or paste
|
||||
`keystore.properties`, `*.jks`, `.env*`, and `android/` are gitignored. Never paste the nsec or keystore password into chat, logs, or command history — keep them in a password manager.
|
||||
Signing material (`keystore.properties`, `*.jks`) lives **outside the repo** at `~/.config/skatespots/android/`; `*.jks`, `.env*`, and `android/` remain gitignored as a safety net. Never paste the nsec or keystore password into chat, logs, or command history — keep them in a password manager. Do not bind the Vite dev server to the LAN; use `adb reverse` for device testing.
|
||||
|
||||
## Project Structure
|
||||
|
||||
|
|
|
|||
19
ZAPSTORE.md
19
ZAPSTORE.md
|
|
@ -156,16 +156,18 @@ Or use Android Studio: right-click `app` → New → Image Asset → import `pub
|
|||
## Step 5 — Generate a signing keystore (one-time)
|
||||
|
||||
```bash
|
||||
install -d -m 700 ~/.config/skatespots/android
|
||||
keytool -genkey -v \
|
||||
-keystore skatespots-release.jks \
|
||||
-keystore ~/.config/skatespots/android/skatespots-release.jks \
|
||||
-alias skatespots \
|
||||
-keyalg RSA \
|
||||
-keysize 2048 \
|
||||
-validity 10000 \
|
||||
-dname "CN=SkateSpots, OU=SkateSpots, O=SkateSpots, L=Bangkok, S=Bangkok, C=TH"
|
||||
chmod 600 ~/.config/skatespots/android/skatespots-release.jks
|
||||
```
|
||||
|
||||
**Save the keystore file and passwords somewhere safe — you need them for every future release.**
|
||||
Put the passwords in `~/.config/skatespots/android/keystore.properties` (mode 600), not in the repo. **Save the keystore file and passwords somewhere safe — you need them for every future release.**
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -175,18 +177,7 @@ keytool -genkey -v \
|
|||
npm run android:build
|
||||
```
|
||||
|
||||
Or manually with signing:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
npx cap sync android
|
||||
cd android
|
||||
./gradlew assembleRelease \
|
||||
-Pandroid.injected.signing.store.file=../skatespots-release.jks \
|
||||
-Pandroid.injected.signing.store.password=YOUR_STORE_PASSWORD \
|
||||
-Pandroid.injected.signing.key.alias=skatespots \
|
||||
-Pandroid.injected.signing.key.password=YOUR_KEY_PASSWORD
|
||||
```
|
||||
`npm run android:build` injects the external signing config automatically. Do **not** pass signing passwords as `-Pandroid.injected...` command-line properties; that exposes them in shell history and process listings.
|
||||
|
||||
APK output: `android/app/build/outputs/apk/release/app-release.apk`
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,12 @@
|
|||
* 1. Removes any previously managed signing block.
|
||||
* 2. Removes a legacy hardcoded signingConfigs block (inline passwords).
|
||||
* 3. Writes a managed signingConfigs block into android/app/build.gradle that
|
||||
* reads secrets from the gitignored keystore.properties at the repo root.
|
||||
* reads secrets from a keystore.properties file outside the repo.
|
||||
*
|
||||
* The signing material is deliberately outside the Vite project root:
|
||||
* ~/.config/skatespots/android/keystore.properties
|
||||
* ~/.config/skatespots/android/skatespots-release.jks
|
||||
* Override the properties path with SKATESPOTS_KEYSTORE_PROPERTIES.
|
||||
*
|
||||
* It exits quietly (0) when android/ isn't generated yet, or when
|
||||
* keystore.properties is absent — so debug-only builds without a keystore still
|
||||
|
|
@ -23,11 +28,16 @@
|
|||
*/
|
||||
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { dirname, join, resolve } from 'node:path';
|
||||
import { homedir } from 'node:os';
|
||||
|
||||
const root = join(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const buildGradle = join(root, 'android', 'app', 'build.gradle');
|
||||
const keystoreProps = join(root, 'keystore.properties');
|
||||
const keystoreProps = resolve(
|
||||
process.env.SKATESPOTS_KEYSTORE_PROPERTIES ??
|
||||
join(homedir(), '.config', 'skatespots', 'android', 'keystore.properties'),
|
||||
);
|
||||
const gradleString = (value) => value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
||||
|
||||
const BEGIN = '// >>> managed by scripts/inject-signing-config.mjs';
|
||||
const END = '// <<< managed by scripts/inject-signing-config.mjs <<<';
|
||||
|
|
@ -43,10 +53,11 @@ if (!existsSync(keystoreProps)) {
|
|||
|
||||
const managedLines = [
|
||||
`${BEGIN} >>>`,
|
||||
'// Release signing is read from keystore.properties at the repo root',
|
||||
'// (gitignored). Edit that file, not this block.',
|
||||
'// Release signing is read from keystore.properties outside the repo.',
|
||||
'// Edit that file, not this block.',
|
||||
'def _keystoreProps = new Properties()',
|
||||
"def _keystorePropsFile = rootProject.file('../keystore.properties')",
|
||||
"def _keystorePropsPath = System.getenv('SKATESPOTS_KEYSTORE_PROPERTIES')",
|
||||
`def _keystorePropsFile = new File(_keystorePropsPath ?: '${gradleString(keystoreProps)}')`,
|
||||
'if (_keystorePropsFile.exists()) {',
|
||||
' _keystorePropsFile.withInputStream { _keystoreProps.load(it) }',
|
||||
'}',
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import { useNostr } from '@nostrify/react';
|
|||
|
||||
/** The SkateSpots CVM server identity (see contextvm.org/s/<pubkey>). */
|
||||
export const CVM_SERVER_PUBKEY =
|
||||
'e3564bb877adb0c99a7e4973ccb22e516d7d9eca0027d0bd62a04174ed670530';
|
||||
'940bf0e4fbc64b6c0f09d48c3264723c524a31b5ad0d1aaec361263731689d95';
|
||||
|
||||
/** The relay the CVM server announces on. */
|
||||
export const CVM_RELAY = 'wss://relay.contextvm.org';
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ Version: v0.1.0`;
|
|||
{ label: t('settingsUi.about.creditMapData'), name: 'BTCMap', url: 'https://btcmap.org/', desc: t('settingsUi.about.creditBtcmap') },
|
||||
{ label: t('settingsUi.about.creditCrowdfunding'), name: 'Angor', url: 'https://angor.io/project/angor1qtunna00uqyx52rr0nvqa059z2gknhrmn7urd6z', desc: t('settingsUi.about.creditAngor') },
|
||||
{ label: t('settingsUi.about.creditDistributed'), name: 'Zapstore', url: 'https://zapstore.dev/apps/org.skatespots.app', desc: t('settingsUi.about.creditZapstore') },
|
||||
{ label: t('settingsUi.about.creditTrust'), name: 'ContextVM', url: 'https://contextvm.org/s/e3564bb877adb0c99a7e4973ccb22e516d7d9eca0027d0bd62a04174ed670530', desc: t('settingsUi.about.creditContextvm') },
|
||||
{ label: t('settingsUi.about.creditTrust'), name: 'ContextVM', url: 'https://contextvm.org/s/940bf0e4fbc64b6c0f09d48c3264723c524a31b5ad0d1aaec361263731689d95', desc: t('settingsUi.about.creditContextvm') },
|
||||
].map((item) => (
|
||||
<a
|
||||
key={item.name}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,26 @@ export default defineConfig(() => ({
|
|||
// The regular web deploy keeps '/' (served from a domain root).
|
||||
base: process.env.CAPACITOR_BUILD ? './' : '/',
|
||||
server: {
|
||||
host: "::",
|
||||
// Keep the dev server off the LAN. The phone preview uses localhost, and
|
||||
// native testing should use `adb reverse` rather than exposing Vite.
|
||||
host: "127.0.0.1",
|
||||
port: 8080,
|
||||
fs: {
|
||||
strict: true,
|
||||
// Defence in depth: secrets live outside the repo, but never serve them
|
||||
// even if someone temporarily copies one back in.
|
||||
deny: [
|
||||
'.env',
|
||||
'.env.*',
|
||||
'*.{crt,pem}',
|
||||
'**/.git/**',
|
||||
'**/keystore.properties',
|
||||
'**/*.jks',
|
||||
'**/*.keystore',
|
||||
'**/*.p12',
|
||||
'**/*.key',
|
||||
],
|
||||
},
|
||||
// Dev proxy for the MintRadar directory (no CORS upstream; see public/_redirects).
|
||||
proxy: {
|
||||
'/api/mints': {
|
||||
|
|
|
|||
Loading…
Reference in a new issue