Getting started
storectl is two CLIs that share auth and an HTTP client. A typical rollout goes metadata first, then billing:
metactl— App Review text metadata (name, descriptions, keywords, review contact, age rating) frommetadata.yaml.iapctl— the billing catalog (subscriptions + one-time products, entitlements, offerings) fromiap.yaml.
Both use the same plan / apply model and are idempotent — safe to re-run.
1. Install
Section titled “1. Install”curl -fsSL https://storectl.dev/install.sh | shmetactl version && iapctl versionThe installer detects your OS/arch, downloads the signed archive, verifies the
checksum (and the cosign signature when cosign is present), and installs both
metactl and iapctl. Pin a version with STORECTL_VERSION, change the target
with STORECTL_INSTALL_DIR, or require signature verification with
STORECTL_VERIFY=1.
2. Provide credentials (env)
Section titled “2. Provide credentials (env)”A provider whose variables are unset is skipped (reported as skip, not an
error), so you can run only the stores you have keys for.
| Provider | Variables |
|---|---|
| App Store Connect | ASC_KEY_ID, ASC_ISSUER_ID, and ASC_PRIVATE_KEY (the .p8 contents) or ASC_PRIVATE_KEY_PATH |
| Google Play | GOOGLE_PLAY_SERVICE_ACCOUNT_JSON (inline) or GOOGLE_PLAY_SERVICE_ACCOUNT_PATH / GOOGLE_APPLICATION_CREDENTIALS |
| RevenueCat (optional, iapctl only) | REVENUECAT_API_KEY (a sk_… V2 secret) |
3. App Review metadata — metactl
Section titled “3. App Review metadata — metactl”Set up the store listing and review info first, from metadata.yaml. See the
metactl page for every field.
# metadata.yaml (excerpt)appName: SonirbundleId: app.sonir.apppackageName: app.sonir.app
review: contactEmail: review@example.com demoAccountRequired: false
defaultLanguage: ja-JPlocales: - locale: ja # ASC "ja"; Play normalized to ja-JP name: "Sonir" subtitle: "空気録音 × 音響測定" shortDescription: "録って、測って、比べる。" description: "…" keywords: "録音,音響,RTA" supportUrl: https://sonir.app/supportmetactl plan -config metadata.yaml # preview — no published writesmetactl apply -config metadata.yaml # write metadata (idempotent)4. Billing catalog — iapctl
Section titled “4. Billing catalog — iapctl”Once the listing exists, provision billing from iap.yaml. One file drives all
three stores; see the iap.yaml reference for every
field.
# iap.yaml (excerpt)appName: SonirbundleId: app.sonir.apppackageName: app.sonir.app
entitlement: key: pro displayName: Pro access
pricing: territory: JPN # App Store territory (ISO-3166 alpha-3) region: JP # Google Play region (ISO-3166 alpha-2) currency: JPY
products: - key: yearly appStoreId: sonir.pro.yearly playProductId: sonir_pro_yearly playBasePlanId: yearly period: P1Y price: 5400 rcPackage: $rc_annual localizations: - { locale: ja, name: "Sonir Pro(年額)", description: "全機能を年額で。" } - { locale: en-US, name: "Sonir Pro (Yearly)", description: "All features, billed yearly." }iapctl plan -config iap.yaml # preview — no writesiapctl apply -config iap.yaml # create missing + correct drift (idempotent)iapctl plan -config iap.yaml -o json # machine-readable reportSymbols (both CLIs): + create · ~ drift · = exists · - skip · ✓ created · ✗ error.
Next steps
Section titled “Next steps”- Metadata details: metactl.
- Wire it into CI: GitOps (PR plan comment, merge apply) — works for both CLIs.
- Catch console changes: Drift detection.
- Full flags: CLI reference.