Skip to content
Open
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
49 changes: 49 additions & 0 deletions .github/workflows/verify-reproducible.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Verify reproducible build

# Rebuilds the app on a clean runner and publishes the unsigned APK's SHA-256, so
# anyone can check a published release against a build nobody could tamper with.
# Needs no secrets: it never signs anything.

on:
push:
branches: [main, master]
tags: ['v*']
pull_request:
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

# Pinned exactly as README-REPRODUCIBLE.md §2 requires. A different JDK build
# can produce a different APK, which is the whole thing we are proving.
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17.0.20+8'

- name: Install the pinned SDK packages
run: |
yes | sdkmanager --licenses >/dev/null 2>&1 || true
sdkmanager "platforms;android-35" "build-tools;35.0.0" >/dev/null

- name: Build (unsigned — no keystore on CI by design)
run: ./gradlew --no-daemon clean assembleRelease

- name: Report the hash
run: |
APK=app/build/outputs/apk/release/app-release-unsigned.apk
SUM=$(sha256sum "$APK" | cut -d' ' -f1)
echo "### Unsigned APK" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "sha256 $SUM" >> $GITHUB_STEP_SUMMARY
echo "bytes $(stat -c%s "$APK")" >> $GITHUB_STEP_SUMMARY
echo "commit $GITHUB_SHA" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY

- uses: actions/upload-artifact@v4
with:
name: app-release-unsigned
path: app/build/outputs/apk/release/app-release-unsigned.apk
60 changes: 60 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Cutting a release

The reproducible build works and `docs/reproducible-build.html` is current. What is
still missing is the path between this repository and the binary:

- the APK is only reachable from `b.pyblock.xyz:8443/app.php`, so someone who finds
the source here cannot get the binary here;
- there are no tags and no releases, so someone holding an APK cannot tell which
commit produced it without reading commit messages. There are now three
`Release build N` commits — 21, 22 and 23 — and not one tag.

A tagged release closes both. Nothing below needs anything this repo does not
already have; the build is already reproducible.

## Per release

```bash
# 1. Tag the commit that produced the build
git tag -a v0.2.3-23 -m "PyBLØCK ᛒ 0.2.3 (build 23)"
git push origin v0.2.3-23

# 2. Build and sign as usual (keystore.properties present)
./gradlew clean assembleRelease

# 3. Publish, attaching the signed APK
gh release create v0.2.3-23 app/build/outputs/apk/release/app-release.apk \
--title "PyBLØCK ᛒ 0.2.3 (build 23)"
```

Release notes worth carrying, all of which you already compute for the docs page:

```
APK SHA-256 6c46683cbf3ad661285faa2ac8399756cf20ac6a7c5ff657762e224c9663237a
Cert SHA-256 e86002aa3ac72325099f92065ec8ab3b7adc70db9e74514ebd53c78acdba3fb5
Unsigned 186f6c9f380298ebd9b0c7d5c63b158b6678b8ec7201e9ca0bdc94b0552a6b27
Reproducible: README-REPRODUCIBLE.md
```

The workflow added alongside this file rebuilds every commit on a clean runner and
prints that unsigned hash in the job summary, so the number can be checked against a
build nobody controlled.

## Zapstore

Zapstore reads GitHub releases, so the tag has to exist first. Publishing links your
APK signing certificate to your nostr identity via NIP-C1 on the first publish —
which is why only the keystore holder can do it.

```bash
go install github.com/zapstore/zsp@latest
zsp publish --wizard # fills zapstore.yaml with your npub — commit it
```

The relay fetches `zapstore.yaml` from this repo, checks the pubkey matches, and
whitelists you automatically. Later releases can use a NIP-46 bunker instead of an
nsec in the environment:

```bash
SIGN_WITH="bunker://..." zsp publish -r github.com/AstrolexisAI/pyblock-blake2b
```
24 changes: 24 additions & 0 deletions zapstore.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Zapstore publishing manifest.
#
# Replace <YOUR_NPUB> with the nostr identity that will publish. The relay fetches
# this file from the repository and checks that the pubkey matches the signer, which
# is how a publisher gets whitelisted automatically.
#
# Generated/validated by: zsp publish --wizard

repository: https://github.com/AstrolexisAI/pyblock-blake2b
pubkey: <YOUR_NPUB>

apps:
- name: PyBLØCK ᛒ
identifier: com.astrolexis.pyblockblake2b
summary: Self-custody Bitcoin wallet for the BLAKE2b proof-of-work fork
description: |
Native Android wallet for Bitcoin-BLAKE2b. On-device keys, coin control,
Ricochet, PayNym, live pool and CHIRP stats, and community Nostr chat.

No Google Play Services: push runs over UnifiedPush, so a distributor such
as ntfy is needed for chat and DM notifications. Reproducible build and
Apache-2.0 — see README-REPRODUCIBLE.md.
license: Apache-2.0
tags: [bitcoin, wallet, blake2b, self-custody, nostr]