Branded app submission: owner intake and operator handoff
TKN-352 extends the existing TKN-337 release tracker. It does not create another release system or submit to either store automatically. The protected internal-upload executor remains limited to TestFlight/internal Play draft uploads. A GitHub handoff packet is not submit readiness.
Responsibility and account choice
| Work | Owner | Taikan/platform operator |
|---|---|---|
| Business name, artwork rights, store name and description | Supplies and explicitly approves | Checks availability, quality and policy fit |
| Support and privacy links, privacy/data-safety declarations, account deletion | Never asked | Owns them as publisher: enters Taikan’s public pages (usetaikan.com/<lang>/privacy-policy, /account-deletion) and the declarations in the store consoles |
| Data collection/sharing/tracking/deletion answers | Supplies business facts per platform | Reconciles facts with the actual app, SDKs and store questionnaires |
| Review steps and seeded demo content | Never asked | Writes the review instructions, arranges secure sign-in, tests access and enters secrets only in the authorized store/private credential channel |
| Screenshots | Reviews exact release media | Captures from the uploaded branded build and records artifact-bound evidence |
| Accounts, IDs, certificates, service-account keys, EAS credentials | Never enters these in Mobile Branding | Configures through existing operator-only controls and approved credential storage |
| Submission/public release | Gives separate, current approvals | Performs manual console actions and records evidence |
For Taikan-published apps, use the existing authorized Taikan Apple/Google accounts; do not create another Taikan account to evade review. Record publisherKind: taikan and an understandable publisher label. Evaluate Apple template/spam guidelines and Google repetitive-content policy before agreeing to proceed. Similar dedicated apps can be rejected or cause account-level enforcement; owner acknowledgement is not an exemption.
For client-owned publication, the client’s legal entity owns and maintains developer enrollment, contracts, fees and store accounts. Obtain delegated operator access through Apple/Google role invitations, never passwords pasted into Taikan. Record publisherKind: client and the client publisher label. Verify app ownership, enrollment, signing, package/bundle identity, EAS project and Firebase mapping before marking provisioning verified. Publisher/account changes require a new candidate and renewed approvals; installed identifiers and permanent link slug remain immutable.
No step in this runbook authorizes changing production accounts, credentials, secrets or infrastructure without the ordinary operational authorization.
Owner intake: Grow → Mobile Branding
- On the Mobile app tab, under App icon and logos, upload each asset separately: the store icon and a visible transparent logo are required; wordmark and splash logo are optional. Each accepts PNG, JPEG or WebP up to 10 MB and 40 megapixels, then opens a crop step; the server crops, resizes and stores a PNG. The icon needs a square area of at least 1024 × 1024 (never upscaled); the crop shows where the stores round the corners, and a transparent icon is refused unless the owner picks a fill colour in that step. Logos, wordmarks and splash logos must end up transparent and are downscaled to fit 4096; a logo on one flat colour (e.g. a JPEG on white) can use Remove background in the crop step, which previews the result on light and dark before upload — a background that is not one flat colour must be exported transparent instead. Animated, disguised, corrupt and fully transparent images are rejected by the server. Icons must not be pre-rounded. Keep key artwork away from corners; Android foregrounds get a central two-thirds contain-fit mark and a crop-safe margin. Preview on both light and dark grounds. The native icon and splash must still be reviewed on a release build.
- Save and publish the member app. Until the icon and logo are published, the App store release section below says so and links back to the assets. Publication copies validated source bytes into new immutable object keys; it does not mutate previous published assets. A publication change supersedes active release candidates.
- In App store release, on the same page, request the dedicated app if necessary. Fill the single submission form’s iPhone and Android sections. Each has its own primary locale, listing name, Apple subtitle / Google short description, full description and explicit content approval. Nothing else is collected: support and privacy links, privacy/data-safety answers, review instructions and demo access belong to Taikan as publisher (see Store disclosures below) and never gate the owner. Submissions saved before this change still hold those keys; the API ignores and strips them on read.
- Attest authority to use the business name and artwork. Save partial work if needed; partial or unapproved intake cannot create a submit-ready candidate. Only the organization owner can save this attestation. The server reports saved readiness separately per platform. A one-platform candidate needs that platform’s complete intake; it need not wait for the other platform.
- Saving intake increments its revision and supersedes active candidates. Editing a platform’s inputs in the form clears that platform’s approval checkbox. New candidates capture the complete intake/revision inside the existing immutable release snapshot. Existing snapshots/hashes are never rewritten. Legacy rows default to no submission/revision zero: historical statuses remain visible, but a fresh approved candidate is required before new review/public-release actions.
Generate and bind a candidate
From the main repository, with operator credentials already available in the environment:
TAIKAN_MOBILE_ROOT=/absolute/path/to/mobile-worktree pnpm brand:org <organization-id-or-slug>
TAIKAN_MOBILE_ROOT=/absolute/path/to/mobile-worktree pnpm brand:org:check <organization-id-or-slug>TAIKAN_API_URL and TAIKAN_API_TOKEN must be set through the approved local credential mechanism. Do not paste token values into shell history, docs, owner fields or release evidence.
The generator derives/checks all assets consumed by current dedicated Expo 55 config:
icon.png: opaque 1024-square PNG with no alpha channel;adaptive-icon.png: centered transparent foreground with safe margins;adaptive-icon-mono.png: white alpha silhouette for themed icons;notification-icon.png: white 96-square alpha notification mark;splash.png: contain-fit optional splash logo, falling back to the logo;feature-graphic.png: opaque 1024 × 500 Play promotional graphic, using the wordmark/logo on the primary background;store-listing.json: the approved intake and revision for the operator to enter into the stores.
The feature graphic is a promotional composite, never a screenshot. No screenshot is generated by brand:org. brand.json records pipeline version 2, exact published source keys/digests, generated output digests and intake. The existing native config verifies the native asset files; the executor verifies every generated output plus source bindings and submission content against the candidate snapshot. Supply the correct google-services.json, then regenerate to capture its digest. Existing association-file and EAS profile generation is unchanged.
Use the existing operator POST /admin/organizations/:orgId/branded-app/releases API to create the candidate after recording the exact mobile source commit. Supply expectedRevision (configuration revision), sourceSha, nativeVersion, selected platforms, manifests (SHA-256 of eas.json, generated brand.json, and pnpm-lock.yaml) and assetDigests from brand.json.sourceAssets. The server verifies source image bytes/digests and captures current approved intake. Commit/review generated assets through the normal release process; never mutate a candidate commit to repair it. Regenerate and create a replacement instead.
The owner completes the current business checks and approves the internal upload. Existing machine/manual upload receipt verification remains authoritative. Do not treat an EAS command exit code, an arbitrary build or a CI artifact alone as a verified upload.
Capture real media
- Download the redacted
brand-build-<operation-id>.jsonreceipt from the protected workflow. It binds source SHA, snapshot hash, native build number and artifact SHA-256. Preserve it in operator evidence, not owner fields. - Install that exact verified uploaded build from TestFlight or the Play internal track. Use a seeded, non-personal demo member in the correct organization. Confirm native app identity/version/build number, published brand version, sign-in, membership lock, primary member flows, notification icon, cold-start splash in both themes, and Universal/App Links. Prevent a different OTA update from replacing the approved source during capture. Expo Go, web previews and development overlays are not evidence of the release binary.
- Capture actual device screens using the device screenshot facility (Android device capture may use
adb exec-out screencap -p; iPhone captures may be exported from the physical device/Xcode device tooling). Use representative member flows and the approved listing locale. No composited logo screens, unrelated demo app, private member data, credentials or artificial screenshots. Do not resize captures to disguise a different device. If no suitable release device is available, leave media blocked. - Current native config has
ios.supportsTablet: false; capture iPhone screens, not iPad. The validator accepts 1–10 distinct iPhone PNGs in supported large-phone sizes (1242×2688, 1284×2778, 1290×2796, 1320×2868, 1260×2736, or landscape equivalents). For Android, supply 2–8 distinct phone PNGs, shortest side at least 320, longest at most 3840 and aspect ratio at most 2:1. Each must be opaque and at most 10 MB. Revisit this contract deliberately if supported native device families or store specifications change. - Make a local capture manifest (paths relative to the manifest):
{
"receipt": "brand-build-<operation-id>.json",
"realBuild": true,
"capturedAt": "2026-09-23T12:00:00.000Z",
"brandManifest": "../brands/north-star/brand.json",
"screenshots": [
{ "file": "schedule.png", "device": "android_phone", "locale": "en" },
{ "file": "program.png", "device": "android_phone", "locale": "en" }
]
}Use device: iphone for iOS. Set the actual capture timestamp after verified upload; never copy the example timestamp. brandManifest is required for Android: the tool verifies its hash against the candidate and imports its generated feature graphic.
From the mobile repository:
node scripts/brand-media.mjs <organization-id> <release-id> <ios-or-android> /absolute/path/capture.jsonThe operator tool reads the current candidate from the existing admin API, compares its verified receipt, uploads files through POST .../releases/:releaseId/evidence-assets, then records the screenshots check on the existing target evidence API. It never calls EAS or store APIs. The server independently checks ownership, content type, actual dimensions/opacity, digest, locale/device set, capture time, source SHA, native build number and artifact digest. Evidence objects receive new server-generated keys with no owner-facing signed URLs. A failure leaves submit readiness blocked; correct the capture and retry against the current target revision. Uploaded but unreferenced objects are not approval evidence.
The API binds evidence to snapshot hash/platform/artifact generation and increments evidence revision. Replacement media invalidates prior approvals; a different artifact/source cannot reuse it. brand-media-<platform>.json is the operator receipt, not owner input. Human attestation is necessary: cryptographic binding identifies the artifact and image bytes, but cannot prove what a camera/device displayed.
Store disclosures and demo verification
Before store_disclosures: pass, the operator must review and enter the exact candidate listing and Taikan’s own public links in the relevant console and compare. Use https://usetaikan.com/<lang>/privacy-policy for the privacy link, the same page or another Taikan page that shows a contact for support, and https://usetaikan.com/account-deletion where Google needs a deletion URL:
- Apple: App Privacy data types, purposes, linking/tracking, SDK data practices, age rating/content rights, account deletion, app access and current review notes.
- Google: Data Safety collection/sharing, encryption/deletion, account deletion URL where required, ads/target audience/content ratings, app access, privacy policy and feature graphic.
- Both: stable public support and privacy pages; brand/name rights and availability; a working demo member with representative content and no purchase requirement; instructions covering any restricted areas. Arrange OTP/bypass access through the approved private channel; put actual review credentials only in the authorized store/private credential system, never evidence notes or owner forms.
Export a redacted PDF/JSON/PNG of the completed disclosures/review configuration (no secrets). In the operator dashboard, Evidence → Store disclosures, upload it and record a note confirming listing, privacy/data-safety and demo-access verification. The API rejects a passing disclosure check without notes and an existing release-scoped attachment. Record associated-domain and internal-smoke checks separately. Record failure when something changes or a demo account stops working; do not leave an old pass as a substitute for current evidence.
Approval and manual store handoff
The owner sees the snapshot-bound listing, real screenshots and Play graphic, not provider controls. Ask them to view every image and approve store review. This explicit consent is separate from intake approval and internal upload; it binds the exact snapshot, artifact generation and evidence revision.
Store-review readiness requires complete current approved intake, all owner business checks, a verified upload, artifact-bound media, operator disclosures, associated-domain checks and internal smoke evidence. Both approval and the eventual operator submitted action re-evaluate readiness. A stale intake/candidate, missing media or old checkbox-only evidence cannot bypass the gate.
Only after that approval should the authorized operator manually submit the exact build/content in App Store Connect or Play Console. Record the actual console observation with the existing store-action API/dashboard and evidence key. Review rejection → record rejection, correct the relevant source/intake/evidence, obtain new consent. Approved by store → record approval, then obtain the separate public-release approval before manually releasing. Never infer public-release authorization from store-review consent. Unknown external outcomes must be reconciled, not blindly retried.
Rollout and verification handoff
Apply additive migration 0149_dusty_lethal_legion through the normal database deployment process before the API. It adds nullable submission JSON and revision zero; it does not modify brand snapshots, release snapshots/hashes or provider configuration. Ship API and dashboard together because owner asset upload is now authenticated multipart, not a presigned-upload exchange. No store account, secret or infrastructure changes are part of this code rollout.
Recommended focused checks from the main repository (written coverage; run during integration):
pnpm test:unit:api src/organizations/branding-assets.unit.spec.ts src/organizations/branding-normalize.unit.spec.ts src/organizations/branding.service.unit.spec.ts src/organizations/branded-app-release.service.unit.spec.ts src/organizations/branded-app-release.controller.unit.spec.ts ../../libs/shared/src/lib/branded-app-releases.unit.spec.ts
pnpm test:unit:web 'src/app/[lang]/(protected)/dashboard/branding/submission-intake.unit.spec.tsx'Then use an authorized local/staging owner session to exercise app asset upload errors and previews, partial/approved per-platform intake, stale-edit conflicts, release listing/media viewing, and approval blocking. Exercise real native assets/screenshots only on a provisioned dedicated release build. Real owner inputs, store enrollment/permissions, platform credentials, demo access, compliant policies, device captures and manual store submission remain operational prerequisites, not fabricated software defaults.