跳到主要内容

Future Prompt — Device Unbind / Transfer Planning

Use this prompt to restart device transfer / unbind development after the 2026-05-13 cloud storage subscription work.

We are in /Users/guorui/Projects/aovis-direct-store, AOVIS Account + Direct Store for aovis.app. Follow AGENTS.md. Keep scope small. Do not touch payment/webhook/auth/prisma schema/admin/deploy unless the step explicitly requires it and stop to explain first.

Context from 2026-05-13:
- Device activation, DeviceOwnership, cloud trial, cloud paid subscription, KVS retention, and Billing Portal are implemented and deployed.
- Test user: [email protected], userId cmne10rir000h8mwi8gvxvwqs.
- Test device: aovis-n4k-000001, Device.id cmp3hyo970000qm97c1foeybk.
- First device has Standard paid cloud storage subscription in Stripe test mode and local entitlement quota=7.
- KVS retention is 720h for all current streams and future streams by default.
- Cloud trial business rule: every physical device gets free Daily Backup Starter Trial only once on first activation; same device must not get a second free trial after transfer/rebind.
- Paid cloud subscriptions are device-scoped. Starter/Standard/Premium share core capabilities; retention quota is the main difference.

Goal:
Design and implement device unbind / transfer / rebind functionality safely, with clear ownership and subscription behavior.

Business principles to preserve:
1. A device can have only one current owner at a time.
2. Device transfer must never grant a second free cloud trial for the same physical device.
3. Same user re-activating same device should be idempotent and should not reset trial or paid subscription periods.
4. Paid cloud subscriptions are device-scoped; before implementation, explicitly decide and document what happens when ownership changes:
- Option A: block transfer while active paid subscription exists and direct current owner to Billing Portal/cancel first.
- Option B: keep subscription with original user and revoke new owner access until new owner subscribes.
- Option C: allow transfer and mark old subscription entitlements expired/canceled locally, while Stripe subscription must still be managed/canceled through Billing Portal.
Start with the safest minimal option unless user chooses otherwise.
5. SIM ownership is device/user-sensitive. Do not silently move SimCard.userId to a new user without explicit transfer logic and audit logs.
6. AWS IoT Thing / KVS Stream / WebRTC Channel usually remain attached to physical device, not user.

Planning tasks:
1. Inspect current models and code:
- Device
- DeviceOwnership
- AuditLog
- SimCard
- Subscription
- Entitlement
- app/api/devices/activate/route.ts
- lib/cloud-trial.ts
- lib/cloud-subscription-entitlements.ts
- lib/device-sim-linking.ts
- lib/aws/device-service-access.ts
2. Produce a short design doc before coding:
- Current ownership model.
- Proposed unbind API.
- Proposed transfer/rebind state machine.
- Paid subscription handling policy.
- Trial no-second-grant protection.
- Audit events.
- Failure and rollback behavior.
3. Prefer a staged implementation:
Phase A: current owner self-unbind for devices with no active paid cloud subscription.
Phase B: transfer/rebind activation flow for a new owner, without second trial.
Phase C: paid subscription edge cases and Billing Portal guidance.
Phase D: UI in Account Devices.
4. Do not implement cross-user transfer until the policy for active paid subscription is explicit.

Suggested minimal Phase A implementation:
- New API route, e.g. POST /api/devices/[id]/unbind or /api/account/devices/[id]/unbind.
- Auth required.
- Current user must be OWNER.
- Block if active Stripe cloud storage Subscription/Entitlement exists for that user/device; return a clear error such as active_subscription_requires_billing_portal.
- If allowed:
- Mark DeviceOwnership inactive/delete? Decide based on schema constraints and current code style.
- Clear Device.ownerUserId only if it points to current user.
- Keep Device pre-provisioning fields intact: awsIotThingName, kvsStreamName, kvsChannelArn, activationCodeHash.
- Do not delete AWS resources.
- Do not delete trial/subscription/entitlement history; expired/revoked entitlements should remain auditable.
- SimCard.userId handling must be explicit; safest initial behavior is do not reassign automatically, record audit if blocked/needs manual policy.
- Write AuditLog event device_unbound.
- Add tests for owner-only, non-owner forbidden, active subscription blocked, AWS fields preserved, trial not reissued.

Acceptance checks:
- npm run test
- npm run build
- Existing device activation dry-run still works.
- Existing cloud storage checkout/Billing Portal routes still work.
- No auth/payment/webhook behavior regression.

Output after implementation:
- Modified files.
- Business decisions made.
- Tests and build result.
- Manual dry-run steps.
- Explicit statement that no second device trial can be granted on rebind.