Prepare Apple App Store distribution (#59)

This commit is contained in:
2026-08-16 17:55:59 +02:00
parent b50e47ab73
commit f894d43eb6
46 changed files with 20694 additions and 166 deletions

View File

@@ -0,0 +1,69 @@
# Apple App Store distribution
Use `distribution/` only as ignored local staging. Never store Apple credentials,
signing material, review credentials, GPG material, application tokens, or
private diagnostics in the repository or release logs.
## Release identity
- iPhone application: `de.rfc1437.ironstorage`
- AutoFill extension: `de.rfc1437.ironstorage.autofill`
- Apple Watch companion: `de.rfc1437.ironstorage.watch`
- Apple Developer team: `MU22FMRGK8`
The submitted archive must embed the Watch application beneath the iPhone
application. An iPhone-only archive is not a releasable IronStorage build.
## Prepare and submit
1. Generate the project and release attribution, then run the release verifier:
```sh
cargo metadata --format-version 1 --filter-platform aarch64-apple-ios \
> /private/tmp/ironstorage-ios-metadata.json
cargo run -p ironstorage-apple-release -- licenses \
/private/tmp/ironstorage-ios-metadata.json \
apple/Resources/App/ThirdPartyLicenses.txt
cargo run -p ironstorage-apple-release -- verify .
```
2. Run the repository gates and build both simulator applications. Validate the
local-first flow with synthetic data: generate or import a GPG key, create
folders and encrypted entries, read TOTP codes, select Watch entries, and
verify the Watch list and selected-code progress display.
3. Create the production archive with normal App Store distribution signing:
```sh
cd apple
xcodegen generate
xcodebuild -project IronStorage.xcodeproj -scheme IronStorage \
-configuration Release -destination 'generic/platform=iOS' \
-archivePath /private/tmp/IronStorage.xcarchive archive
```
4. Inspect the archive before upload. The iPhone, AutoFill, and Watch bundle
identifiers and versions must match the metadata; the Watch app must be
embedded; privacy manifests and license resources must be present; the
archive must use distribution signing and must not contain `get-task-allow`,
simulator artifacts, synthetic demo data, private endpoints, or secrets.
5. Export and upload with the checked-in App Store Connect export options:
```sh
xcodebuild -exportArchive \
-archivePath /private/tmp/IronStorage.xcarchive \
-exportPath /private/tmp/IronStorage-AppStore \
-exportOptionsPlist AppStore/ExportOptions.plist
```
6. In App Store Connect, complete the listing from `metadata.toml`, upload the
reviewed screenshots, answer privacy and encryption questions, provide
local-first review instructions, select the processed build, and submit it
for review. Do not provide a production password store or real credentials.
7. After approval, install the public release from the App Store on the paired
iPhone and Apple Watch. Verify local onboarding, key overwrite confirmation,
local Git history, folders, encrypted entries, Face ID, TOTP, optional HTTPS
pull and push, Watch snapshot delivery, the Watch entry-only list, and the
larger selected code with its validity progress indicator.
8. Verify an App Store update in place. Verify the documented uninstall and
reinstall Keychain lifecycle only with disposable test data; never erase the
paired physical device or its unrelated IronStorage data.

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>destination</key>
<string>upload</string>
<key>manageAppVersionAndBuildNumber</key>
<false/>
<key>method</key>
<string>app-store-connect</string>
<key>signingStyle</key>
<string>automatic</string>
<key>stripSwiftSymbols</key>
<true/>
<key>teamID</key>
<string>MU22FMRGK8</string>
<key>uploadSymbols</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,52 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>rfc1437 Apps</title>
<link rel="stylesheet" href="/assets/pico.pumpkin.min.css">
<link rel="stylesheet" href="/assets/bds.css">
<style>.app-header{display:flex;align-items:center;gap:1rem}.app-header img{width:5rem;height:5rem;border-radius:1.1rem}.gallery{display:grid;grid-template-columns:repeat(auto-fit,minmax(14rem,1fr));gap:1rem}figure{margin:0}figure img{display:block;width:100%;height:auto;border-radius:1.5rem}figcaption{margin-top:.5rem;color:var(--pico-muted-color)}</style>
</head>
<body><main>
<header><h1 class="archive-heading">rfc1437 Apps</h1><p>Small, focused apps published by <a href="https://rfc1437.de/">rfc1437</a>.</p></header>
<nav class="blog-menu" aria-label="Site navigation"><ul class="blog-menu-list"><li class="blog-menu-item"><a class="blog-menu-link" href="https://rfc1437.de/">Home</a></li><li class="blog-menu-item"><span class="blog-menu-link" aria-current="page">Apps</span></li></ul></nav>
<article class="post">
<h2>Install</h2>
<p>Gotcha is available through the rfc1437 AltStore PAL source at <code>https://rfc1437.de/apps/source.json</code>.</p>
<p>IronStorage is being prepared for the Apple App Store so its iPhone app and Apple Watch companion install together through the supported App Store path.</p>
</article>
<section><h2>Apps</h2>
<article class="post">
<header class="app-header"><img src="gotcha/icon.png" alt="Gotcha app icon"><div><h3>Gotcha</h3><p>Native Gitea and Forgejo on iPhone.</p></div></header>
<p>Follow repositories, issues, pull requests, commits, diffs, and milestones on servers you control.</p>
<nav><a href="gotcha/privacy/">Privacy</a> · <a href="https://git.rfc1437.de/hugo/Gotcha">Source</a> · <a href="https://git.rfc1437.de/hugo/Gotcha/issues">Support</a></nav>
<div class="gallery">
<figure><img src="gotcha/screenshots/01-home-activity.png" alt="Gotcha activity"><figcaption>Activity</figcaption></figure>
<figure><img src="gotcha/screenshots/02-issues.png" alt="Gotcha issues"><figcaption>Issues</figcaption></figure>
<figure><img src="gotcha/screenshots/03-commit-history.png" alt="Gotcha commits"><figcaption>Commits</figcaption></figure>
<figure><img src="gotcha/screenshots/04-diff.png" alt="Gotcha diff"><figcaption>Diffs</figcaption></figure>
<figure><img src="gotcha/screenshots/05-pull-request.png" alt="Gotcha pull request"><figcaption>Pull requests</figcaption></figure>
<figure><img src="gotcha/screenshots/06-milestones.png" alt="Gotcha milestones"><figcaption>Milestones</figcaption></figure>
</div>
</article>
<article class="post">
<header class="app-header"><img src="ironstorage/icon.png" alt="IronStorage app icon"><div><h3>IronStorage</h3><p>Native pass and pass-otp on iPhone and Apple Watch.</p></div></header>
<p>Generate or import a protected GPG key, keep local Git history, create encrypted entries and TOTP codes, and add optional HTTPS synchronization later.</p>
<p><strong>Apple App Store release in preparation.</strong></p>
<nav><a href="ironstorage/privacy/">Privacy</a> · <a href="https://git.rfc1437.de/hugo/IronStorage">Source</a> · <a href="https://git.rfc1437.de/hugo/IronStorage/issues">Support</a></nav>
<div class="gallery">
<figure><img src="ironstorage/screenshots/01-home.png" alt="IronStorage local Git home"><figcaption>Local Git status</figcaption></figure>
<figure><img src="ironstorage/screenshots/02-passwords.png" alt="IronStorage password folders"><figcaption>Passwords and folders</figcaption></figure>
<figure><img src="ironstorage/screenshots/03-totp.png" alt="IronStorage TOTP list"><figcaption>TOTP and Watch selection</figcaption></figure>
<figure><img src="ironstorage/screenshots/04-totp-detail.png" alt="IronStorage TOTP detail"><figcaption>Live TOTP detail</figcaption></figure>
<figure><img src="ironstorage/screenshots/05-preferences.png" alt="IronStorage preferences"><figcaption>Local-first preferences</figcaption></figure>
<figure><img src="ironstorage/screenshots/06-search.png" alt="IronStorage search"><figcaption>Password search</figcaption></figure>
<figure><img src="ironstorage/screenshots/watch-01-list.png" alt="IronStorage Watch entry list"><figcaption>Watch entry list</figcaption></figure>
<figure><img src="ironstorage/screenshots/watch-02-detail.png" alt="IronStorage Watch code detail"><figcaption>Watch code and progress</figcaption></figure>
</div>
</article>
</section>
<footer><small>Both apps are free and open-source software. Each listing identifies its supported distribution channel.</small></footer>
</main></body></html>

26
apple/AppStore/index.html Normal file
View File

@@ -0,0 +1,26 @@
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>IronStorage</title>
<style>body{font:17px system-ui;line-height:1.55;max-width:64rem;margin:3rem auto;padding:0 1rem;color:#1d1d1f}header{display:flex;align-items:center;gap:1rem}header img{width:6rem;border-radius:1.3rem}.gallery{display:grid;grid-template-columns:repeat(auto-fit,minmax(14rem,1fr));gap:1rem}.gallery img{width:100%;border-radius:1.5rem}nav{margin:1.5rem 0}</style>
<header><img src="icon.png" alt="IronStorage app icon"><div><h1>IronStorage</h1><p>Native pass and pass-otp for iPhone and Apple Watch.</p></div></header>
<p>Start locally with a protected GPG key and versioned password store. Create folders and encrypted entries on your iPhone, generate TOTP codes, and optionally connect an HTTPS Git remote later.</p>
<p>The Apple Watch companion receives only the TOTP entries you explicitly share.</p>
<p><strong>Apple App Store release in preparation.</strong></p>
<nav><a href="privacy/">Privacy</a> · <a href="https://git.rfc1437.de/hugo/IronStorage">Source code</a> · <a href="https://git.rfc1437.de/hugo/IronStorage/issues">Support</a></nav>
<div class="gallery">
<img src="screenshots/01-home.png" alt="Local Git status">
<img src="screenshots/02-passwords.png" alt="Password entries and folders">
<img src="screenshots/03-totp.png" alt="TOTP entries selected for Apple Watch">
<img src="screenshots/04-totp-detail.png" alt="Live TOTP detail">
<img src="screenshots/05-preferences.png" alt="Local-first preferences">
<img src="screenshots/06-search.png" alt="Password search">
</div>
<h2>Apple Watch</h2>
<div class="gallery">
<img src="screenshots/watch-01-list.png" alt="Apple Watch TOTP entry list">
<img src="screenshots/watch-02-detail.png" alt="Apple Watch selected code and progress bar">
</div>
</html>

View File

@@ -0,0 +1,74 @@
[app]
name = "IronStorage"
bundle_identifier = "de.rfc1437.ironstorage"
developer_name = "rfc1437"
subtitle = "Native pass on iPhone"
description = "IronStorage is a native iPhone and Apple Watch client for pass and pass-otp password stores. Start entirely on-device with a protected GPG key and local Git history, then add optional HTTPS Git synchronization whenever you want it."
keywords = "password,gpg,pass,totp,git,security,watch"
icon_file = "apple/Assets.xcassets/AppIcon.appiconset/AppIcon.png"
category = "utilities"
privacy_url = "https://rfc1437.de/apps/ironstorage/privacy/"
support_url = "https://git.rfc1437.de/hugo/IronStorage/issues"
marketing_url = "https://rfc1437.de/apps/ironstorage/"
age_rating = "4+"
[release]
version = "0.1.0"
build = "1"
minimum_ios = "17.0"
notes = "Initial iPhone and Apple Watch release with local GPG key generation, pass-compatible local Git history, optional HTTPS synchronization, password folders, and explicitly shared TOTP codes on Apple Watch."
[[screenshots]]
file = "apple/AppStore/screenshots/home.png"
caption = "Local Git history works before a sync remote is configured"
width = 1206
height = 2622
[[screenshots]]
file = "apple/AppStore/screenshots/passwords.png"
caption = "Password entries and folders stay organized on device"
width = 1206
height = 2622
[[screenshots]]
file = "apple/AppStore/screenshots/totp.png"
caption = "TOTP entries are shared with Apple Watch only when selected"
width = 1206
height = 2622
[[screenshots]]
file = "apple/AppStore/screenshots/totp-detail.png"
caption = "Live TOTP codes include an exact validity indicator"
width = 1206
height = 2622
[[screenshots]]
file = "apple/AppStore/screenshots/preferences.png"
caption = "Generate or import a GPG key and add optional HTTPS sync later"
width = 1206
height = 2622
[[screenshots]]
file = "apple/AppStore/screenshots/search.png"
caption = "Search encrypted password entries by name or folder"
width = 1206
height = 2622
[[watch_screenshots]]
file = "apple/AppStore/screenshots/watch-list.png"
caption = "The Watch list shows only explicitly shared TOTP entries"
width = 416
height = 496
[[watch_screenshots]]
file = "apple/AppStore/screenshots/watch-detail.png"
caption = "The selected Watch code is large with a validity progress bar"
width = 416
height = 496
[content]
advertising = false
account_creation = false
digital_purchases = false
tracking = false
user_generated_content = false

View File

@@ -0,0 +1,18 @@
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>IronStorage Privacy</title>
<style>body{font:17px system-ui;line-height:1.55;max-width:44rem;margin:3rem auto;padding:0 1rem;color:#1d1d1f}h1,h2{line-height:1.2}</style>
<h1>IronStorage Privacy</h1>
<p>Last updated: 16 August 2026</p>
<p>IronStorage does not collect analytics, advertising identifiers, diagnostics, or personal data for the developer, and it does not track you.</p>
<h2>Data on your device</h2>
<p>Your local Git repository and encrypted password entries remain on your device. Application tokens, imported or generated private-key material, and protected unlock data use Apple Keychain storage. Camera frames used to scan a key-transfer QR code are processed on the device and are not retained by IronStorage.</p>
<h2>Network access</h2>
<p>IronStorage works without a network service. If you later configure an HTTPS Git server, it connects only to that server using the credentials you provide to pull and push your password store. The developer does not operate an IronStorage service and does not receive this traffic.</p>
<h2>Deletion</h2>
<p>Remove configured credentials and key material in IronStorage before uninstalling when you want them deleted immediately. Keychain items otherwise follow Apples secure-storage lifecycle and may survive an app reinstall.</p>
<h2>Support</h2>
<p>Questions and issues can be filed at <a href="https://git.rfc1437.de/hugo/IronStorage/issues">the IronStorage issue tracker</a>.</p>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB