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
71 changes: 71 additions & 0 deletions .github/workflows/play-bundle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Play release bundle
on:
push:
branches: [main]
paths: ['app/**', 'i18n/**', 'gradle/**', '*.gradle.kts', 'gradle.properties', '.github/workflows/play-bundle.yml']
workflow_dispatch:
permissions:
contents: read
concurrency:
group: play-bundle
cancel-in-progress: false
jobs:
bundle:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: play-internal
timeout-minutes: 30
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
with:
persist-credentials: false
- uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3
with:
distribution: temurin
java-version: '21'
- name: Release tests and lint
run: ./gradlew testReleaseUnitTest lintRelease --no-daemon --no-configuration-cache
- name: Sign release bundle
env:
KEY_BASE64: ${{ secrets.PINAKES_KEYSTORE_BASE64 }}
PINAKES_KEYSTORE_PASSWORD: ${{ secrets.PINAKES_KEYSTORE_PASSWORD }}
PINAKES_KEY_ALIAS: ${{ secrets.PINAKES_KEY_ALIAS }}
PINAKES_KEY_PASSWORD: ${{ secrets.PINAKES_KEY_PASSWORD }}
run: |
set -euo pipefail
umask 077
test -n "$KEY_BASE64"
test -n "$PINAKES_KEYSTORE_PASSWORD"
test -n "$PINAKES_KEY_ALIAS"
test -n "$PINAKES_KEY_PASSWORD"
export PINAKES_KEYSTORE="$RUNNER_TEMP/pinakes-upload.jks"
trap 'rm -f "$PINAKES_KEYSTORE"' EXIT
printf '%s' "$KEY_BASE64" | base64 --decode > "$PINAKES_KEYSTORE"
./gradlew bundleRelease --no-daemon --no-configuration-cache
# `jarsigner -verify` exits 0 on an UNSIGNED artifact — it just prints
# "jar is unsigned". Since the build deliberately falls back to an
# unsigned bundle when the credentials do not reach Gradle, an exit
# code alone would let an unsigned bundle pass as verified. Require
# the positive statement instead.
verify_out="$(jarsigner -verify app/build/outputs/bundle/release/app-release.aab)"
printf '%s\n' "$verify_out"
case "$verify_out" in
*"jar verified"*) ;;
*) echo "::error::release bundle is not signed"; exit 1 ;;
esac
sha256sum app/build/outputs/bundle/release/app-release.aab > app/build/outputs/bundle/release/SHA256SUMS.txt
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: pinakes-play-${{ github.sha }}
path: |
app/build/outputs/bundle/release/*.aab
app/build/outputs/bundle/release/SHA256SUMS.txt
app/build/outputs/mapping/release/mapping.txt
if-no-files-found: error
retention-days: 30
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
if: always()
with:
name: play-reports-${{ github.sha }}
path: app/build/reports/
retention-days: 14
57 changes: 40 additions & 17 deletions PRIVACY.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# Privacy Policy — Pinakes Android

_Last updated: 2026-06-19_
_Technical privacy description updated: 2026-09-09. The publisher has confirmed
the controller and contact below. Retention details and the remaining release
gates still need verification before this becomes the final Play privacy policy._

Pinakes Android is an open-source client app that connects to a **Pinakes
library instance chosen by you**. The app itself has no backend of its own: all
your data lives on the library server you point it at, operated by that library.
library instance chosen by you**. Library services use that instance's server.
The app also integrates Sentry for crash diagnostics and loads media from URLs
supplied by the library; not all requests are limited to the library's domain.

## What the app stores on your device

Expand All @@ -22,15 +25,22 @@ when you log out or disconnect from the instance.

## What the app sends, and to whom

The app communicates **only** with the Pinakes instance URL you configure. It
For library services, the app communicates with the Pinakes instance you configure. It
sends your email and password (over HTTPS) at login, and your bearer token on
subsequent requests, to authenticate and to perform the actions you initiate
(search, loans/reservations, wishlist, profile edits, contact messages). It also
fetches book cover images from URLs returned by that instance.

The app does **not** include third-party analytics, advertising, or tracking
SDKs, and does not transmit your data to the app's authors or any party other
than your chosen library instance.
The app includes **Sentry crash reporting**, configured during app startup.
Default PII attachment is disabled and performance trace sampling is zero.
These settings do not mean no data is transmitted: error events, technical
device/app information and diagnostic context can reach Sentry. Retention and
the precise diagnostic fields must be verified against the publisher's Sentry
configuration before completing the Play Data Safety declaration.

Catalog and HTTP caches are also stored locally. Signing out or switching
instances clears the relevant caches. Signing out revokes the session; it does
**not** delete the account, loans or other records held by the library.

## Network security

Expand All @@ -40,17 +50,30 @@ required to be HTTPS.

## Push notifications (optional)

When enabled by your library and by you, push delivery uses **UnifiedPush**
through a distributor of your choosing. Registration data (a push endpoint and
WebPush keys) is sent to your library instance only.
The app has a notification feed and UnifiedPush-related API support. A complete
distributor integration has not been verified for this release; do not describe
background push delivery as universally available. Any enabled distributor and
its data handling must be included in the deployment's privacy review.

## Data controller

Your data is controlled by the **library operating the Pinakes instance** you
connect to. For requests about access, correction, or deletion of your account
data, contact that library directly. For questions about the app itself, open an
issue on the project repository.
The publisher and controller for this Pinakes Android distribution and its app
diagnostics is **D'Alessandro Fabio Gaetano**, reachable at
**[info@fabiodalez.it](mailto:info@fabiodalez.it)**. This is also the contact for
the reference library at **https://biblioteca.fabiodalez.it**.

When you connect to another independently operated library, that library
operates its own account and circulation services. Consult its privacy policy
and contact it for access, correction or deletion requests relating to those
records. Do not post credentials, loan history or other private data in public
GitHub issues. Never send your password in a privacy request.

The app supports account registration. A compliant in-app account-deletion
request path and an external request URL remain release gates; logout must not
be presented as account deletion. A library may need to retain specific records
under its obligations, which its policy must explain accurately.

> Libraries publishing their own build: replace this section with your
> organisation's contact details and host this policy at a public URL, then link
> that URL in the Google Play Console (App content → Privacy policy).
For the reference library, account and privacy requests may be sent to
info@fabiodalez.it, identifying the library URL and account email. Identity
verification may be necessary before processing a request. This contact does
not yet replace the in-app and public-web deletion paths listed above.
15 changes: 15 additions & 0 deletions docs/PLAY-RELEASES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Play releases

GitHub is the source of truth. Pull requests run Android CI. After merging Android changes into main, `Play release bundle` runs release unit tests and lint, then signs an AAB and retains the bundle, checksum, R8 mapping and reports as GitHub artifacts. Manual dispatch is supported on main only. No PR code receives signing secrets.

Environment `play-internal` must allow main only. Required environment secrets: `PINAKES_KEYSTORE_BASE64`, `PINAKES_KEYSTORE_PASSWORD`, `PINAKES_KEY_ALIAS`, `PINAKES_KEY_PASSWORD`. Use the existing release key; never generate a replacement silently. Keys are materialized in runner temporary storage and removed after signing; configuration caching is disabled.

This workflow builds bundles; it does NOT upload to Play or publish to production. Publisher API credentials and app-scoped Play permissions are still required for that separate stage. Increment versionCode before each new Play upload, including corrective rebuilds after a code has been accepted. Debug GitHub releases keep their existing path and are not Play artifacts.

Review library: https://biblioteca.fabiodalez.it. Mobile API was enabled on 2026-09-09. A dedicated standard reader was created; credentials are private outside Git, not in this document or release artifacts.

Publisher/controller confirmed by the owner on 2026-09-09: **D'Alessandro Fabio Gaetano**. Public support/privacy email: **info@fabiodalez.it**. Use these for this distribution, not as the controller of independently operated third-party library instances.

Remaining release gates: accurate privacy policy including Sentry, account-deletion path and public request URL, reviewer access test, minified-device smoke test, native 16 KB compatibility and Play questionnaires. The personal developer account requires the closed-test period before production access. Do not interpret a green bundle build as policy approval.

The older PLAY_STORE_COMPLIANCE.md audit is historical: its no-third-party-SDK and disabled-R8 statements no longer describe the app. Sentry is present, R8 is enabled, and logout does not delete the user's server account.
Loading