Skip to content

How it works

Three ways to run

runAudit() branches three ways depending on what you’ve given it:

ModeTriggerWhat runs
LiveCredentials configured (.p8 + Key ID + Issuer ID)The full App Store Connect API sweep, plus local project analysis, plus bundle-id / version cross-checks.
Local-onlyA project selected, no credentialsOnly the local static analysis checks (see Xcode Project).
DemoNeitherA bundled fixture (CircleMind sample) so you can explore the UI offline.

Authentication

  • Base URL: https://api.appstoreconnect.apple.com/v1.
  • Auth is a JWT bearer token, algorithm ES256, signed with your downloaded .p8 private key using Apple’s CryptoKit (P256.Signing).
  • Tokens are capped by Apple at 20 minutes; AuditStore uses ~19 and caches the token in memory, regenerating when it’s older than ~15 minutes.
  • You generate the key in App Store Connect → Users and Access → Integrations → App Store Connect API (needs Admin or App Manager role). The .p8 can only be downloaded once.

The API is JSON:API. Responses carry data, attributes, and relationships — and each relationship carries a full links.related URL.

AuditStore follows those relationship links rather than hardcoding nested endpoint paths. This keeps the app working even if Apple reshuffles secondary endpoints. The entry point is a single lookup:

GET /apps?filter[bundleId]=<your bundle id>

From that one app record, every other section is reached by following links: appInfos, appStoreVersions, appPriceSchedule, subscriptionGroups, and so on.

How a check can’t lie

A check that can’t be evaluated becomes an .errored row carrying the raw App Store Connect message. It never crashes, and — critically — it never silently passes. An unevaluated section is surfaced as an error, not a green check.

  • 401 / auth failure on the entry-point lookup blocks every ASC-derived section with the same error, rather than leaving them misleadingly empty.
  • 404 usually means not created yet — often a finding, not a crash.
  • Explicit JSON null is distinguished from an absent key.

The scan lifecycle

The live run advances through ordered stages so the progress UI moves monotonically:

  1. Authenticating — sign the JWT, look up the app by bundle id.
  2. Fetching metadata — app info, age rating, privacy, category, pricing, encryption.
  3. Screenshots — per-locale metadata and screenshot sets for the latest editable version.
  4. Subscriptions — subscription groups (if enabled in settings).
  5. Build & compliance — attached build, processing state, export compliance, and review details.
  6. Compiling — aggregate into the final Report.

Local static analysis

The macOS sandbox can’t spawn xcodebuild, so the analyzer parses project.pbxproj directly (via PropertyListSerialization, which reads the OpenStep format) and walks your Swift/ObjC sources (bounded to 600 files to stay responsive). It resolves the Release build settings for the app target and:

  • Detects privacy-sensitive API usage and cross-checks it against your Info.plist purpose strings and your App Privacy declaration.
  • Infers an app-type profile (login, tracking, IAP, account system) that gates the Review Guidelines checklist.
  • Cross-checks the project’s bundle id and marketing version against the live App Store Connect app.

If a build setting can’t be statically resolved (an .xcconfig base, or unexpanded $(…) variables), the analyzer emits an Info caveat instead of confidently passing or mis-detecting.


Next: Checks

AuditStore is a macOS app by NativLab. Runs entirely on your Mac — your API private key never leaves your device.