smbcloud-ascapi — upload + signing-identity hardening: plan (closed)
Findings from the Siti AI release session (macOS App Store upload of
Siti AI.pkg through App Store Connect). Repo:
github.com:smbcloudXYZ/smbcloud-ascapi, base branch development, remote
setoelkahfi (there is no origin, so use git/gh, not the sigit MCP
tools).
Companion plan: feature-plan-from-klepon-xcode-release-session.md
— that session shipped the builds API + token-source + altool-upload stack
(gh stack #6→#7→#8→#10). Both have now closed out.
Closed-out progress (2025-10-11, this session): gh stack #27
(bottom→top) #24 docs/list-builds-pagination → #25 feat/signing-doctor
→ #26 feat/apps-await-processing, rebased onto development, all
CI-clean (fmt / clippy -D warnings / test / RUSTDOCFLAGS=-D warnings
doc / lower-crate solo builds), signed+verified, auto-merge enabled.
This landed finding #2 (signing doctor), finding #5
(await-processing), and finding #6 (list_builds pagination doc).
Closed-out progress (later): the upload.rs cluster
(findings #1, #3, #4 — pre-flight --validate-app, body-aware
success, .app-vs-.pkg distinction) shipped together on the same
file, sharing the test scaffolding that already covered altool_args.
Every finding from this plan is now in development.
- Both sessions hit the same root pain: a stale private key / signing
identity with the right id but wrong contents, which surfaces as an
indistinguishable 401 (klepon plan, "Separate / unaddressed bug") or, in
this session, ~20 min lost to a stale
SIGNING_IDENTITYin.env.appstore.ascapi apps uploadalready dodges the key half by passing--p8-file-path(klepon #10); the local signing identity half (finding #2 below) is now covered byasc signing doctor. - Klepon's open "Live smoke test against ASC" item is the natural place to also exercise findings #1 and #3 once implemented. Still blocked on credentials.
Session context
- Uploaded a macOS build (
Siti AI.pkg) to ASC successfully. The time sink was a staleSIGNING_IDENTITYin.env.appstore— nothing in ascapi inspects the local keychain, so the tool gave no signal it was stale. ascapidoes own upload + TestFlight/review orchestration (asc apps upload,xcrun altool). It cannot build or sign — out of scope by design. The findings below stay inside what ascapi already owns.
Findings
1. No pre-upload altool --validate-app pass — HIGH ✅ SHIPPED
frontend::upload::validate_app (added in
crates/frontend/src/upload.rs) is now a server-side check: it runs
xcrun altool --validate-app with the same --p8-file-path auth parity
as the upload path, surfacing Apple's JSON verdict. Reachable from the CLI
as asc apps validate <pkg>. --wait is deliberately not passed on the
validation path — validation is already synchronous.
- Value: turns a ~minutes-late failure into a seconds-early one; catches
the common "wrong version / bad entitlement" class before upload.
2. No local-keychain signing doctor — HIGH ✅ SHIPPED (PR #25)
The signing crate (certificate.rs, profile.rs, csr.rs) talks only
to the ASC REST API. security find-identity is called nowhere in the
repo; there is no local-keychain code at all. So the exact stale-identity
problem this session hit (and the klepon 401 key-drift bug) has zero
coverage: ascapi can tell you what certs/profiles Apple has, but not
what's installed and valid locally, nor whether they line up.
- Fix: add asc signing doctor that runs security find-identity -v
-p codesigning, lists local valid signing identities, and cross-checks
them against the certificates the ASC API reports
(CertificateApi::list_certificates) — flagging identities that are
expired, missing locally, or present locally but revoked/absent on Apple.
Optionally resolve the .p8 the CLI would use and confirm it exists and
is readable.
- Value: directly closes the gap that cost ~20 min this session and is
the local half of klepon's key-drift bug. Bigger surface (new keychain
interaction, macOS-only) → its own branch.
3. success trusts the exit code, not the JSON body — HIGH ✅ SHIPPED
outcome() (crates/frontend/src/upload.rs) now derives success from
exit code and the parsed body — a non-empty product-errors /
product_errors / errors array under any of altool's key spellings
counts as failure even on exit 0. Exit code stays in UploadOutcome for
debugging.
- Value: small, same-file change; removes false "success" reports.
4. .app-vs-.pkg gives a generic "does not exist" — MEDIUM ✅ SHIPPED
In validate_package, a .app bundle (a directory) now hits a
precise message: "this is an .app bundle, not an uploadable package;
export the archive first (xcodebuild -exportArchive) to get an
.ipa/.pkg". A bare directory still reports "is a directory, not a
package file" so "path missing" and "wrong kind" stay distinct.
- Value: tiny, same-file, removes a recurring confusing error.
5. No non-blocking await-processing loop — MEDIUM ✅ SHIPPED (PR #26)
The only way to wait for ASC to finish processing a build is altool's
--wait, which blocks the altool process for the whole duration. There's
no poll-based path that uses the builds API (BuildsApi::list_builds,
shipped in klepon #8) to report processing state incrementally or let the
caller poll on its own cadence.
- Fix: add asc apps await-processing <build-id|--build-number> that
polls /v1/builds for processingState until VALID/FAILED/timeout,
with backoff. Complements klepon's builds API; an MCP-friendly
alternative to a blocking --wait.
- Value: better UX for CI/agents; builds on existing klepon surface.
- Shipped (PR #26): asc apps await-processing <APP_ID> with
--version, --pre-release-version, --interval (30s),
--timeout (1800s). Shared poll loop + pure classify() /
AwaitConfig / AwaitOutcome in crates/frontend/src/await_processing.rs
(serializes without key material; 7 in-module tests). Terminal map:
VALID → success, INVALID/FAILED → failure, else timeout → non-zero exit.
CLI-only for now; the pure core is MCP-ready if a tool is added later.
6. list_builds limit=200 pagination — LOW (doc-only) ✅ SHIPPED (PR #24)
list_builds caps at limit=200 with no cursor follow-through. Fine for
normal apps; note it as a known limit. Overlaps klepon's open "MCP
list_builds doesn't expose BuildFilter" item — fold into that rather
than tracking separately.
Status
Closed. All six findings merged. The only remaining item is the live ASC smoke test, shared with the klepon plan and credential-blocked.