Current-code mapping, migration and design self-audit
Historical design snapshot
This page records the earlier design scope, not current release status. Agentfy now has committed provisioning, scoped authorization and trusted MCP foundations; see the implemented server-chat contract and evidence boundary. Browser/BONSITE protocols and broader billing/lifecycle proposals below retain their own release gates. No live partner deployment is implied.
Current source evidence
Source paths below are under api/src/slices/ unless the full api/ prefix is shown. These facts were read in the AGNT2-424 checkout; proposed changes must not be mistaken for current behavior.
| Current evidence | Consequence and required migration |
|---|---|
api/src/slices/user/user/user.prisma: mandatory unique email, optional password, platformRole | Expand to a credential-independent actor and nullable/non-applicable email for partner actors; no dummy addresses. Direct credential uniqueness remains enforced. |
user/auth/domain/auth.types.ts: IAuthUser and user JWT require email; token union is access/refresh/session | Introduce explicit scoped principal/source/audience contracts; version DTOs and regenerate SDK, do not casually rename fields or broaden the global guard. |
user/auth/domain/auth.service.ts: registration claims passwordless email stubs; new users get a team; registration invokes founding | Dedicated credential-only/link and partner bootstrap flows; no email-stub takeover, extra team or partner founder promotion. Audit founding selection to exclude external actors. |
user/auth/guards/jwtAuth.guard.ts: access signature/type checked, request receives sub/email | State-backed revocation and explicit endpoint opt-in for partner/linked principals; account, platform and financial-consent routes demand direct proof. |
user/auth/domain/auth.service.ts: refresh verifies token type and user existence, then issues tokens | Add rotating persisted session families, source/epoch/version checks and reuse detection before claiming unlink/revoke works. Current code does not implement this. |
user/team/domain/team.service.ts: team/key, owner seat and provisioning are separate awaited writes | Replace provisioning entry with an explicit transactional allocation gateway plus durable outbox; do not describe the current method as atomic. |
user/team/domain/teamProvision.port.ts: handler exceptions logged/swallowed | Persist readiness/retries; a successful current signup is not proof concierge setup succeeded. |
user/userTeam/userTeam.prisma: unique actor/team; role string permits multiple owners; domain/userTeam.service.ts counts owners before mutation | Retain role as ownership source; serialize last-owner/transfer operations, handle inactive owners and security suspension. |
agent/agent/agent.prisma: composite Agent(ownerId, teamId) references UserTeam(userId, teamId) | Keep actor IDs stable; reassign agents or preserve inactive memberships and suspend jobs before removal. |
billing/{subscription,invoice,payment,paymentMethod}/*.prisma: team-scoped commercial records; invoice unique subscription/period, team cascade | Add payer timelines and retained invoice lines without deleting history or moving old provider subscriptions; remove financial cascade risk before enabling new billing. |
billing/invoice/domain/invoice.service.ts, billing/payment/domain/payment.service.ts: invoices written on completed-payment intake | Current PayPal subscription flow is not a usage-invoicing engine; certify mandate/cancellation/callback behavior for both payer modes. |
system/usage/meter/meter.prisma: frozen costUsdPico, IDs without resource FKs; raw-event rollups | Preserve ledger and precision, add durable historical commercial attribution to events/aggregates before live payer changes. |
CleanSlice mapping
Consulted CleanSlice MCP get-started, list-categories, searches for cross-slice authentication/organization dependencies and transactions/idempotency/billing, and full docs/architecture/dependency-flow.md. The second search returned no matching transaction guide; the transaction proposal here is an explicit design choice, not an invented MCP rule. The project layers and constitution further constrain dependencies.
Organization/identity/membership contracts belong in singular user-group slices; user auth owns browser/link contexts. Billing (L6) coordinates provisioning that requires both user allocation (L2) and payer state, using a domain gateway contract whose data implementation owns the local transaction. The implementation plan must specify a shared transaction boundary for those writes, not call independent service methods and label that atomic. Agent setup (L5) consumes the durable operation through downward contracts; no remote effect is inside the local transaction.
Lower system usage (L1) must not import user or billing. It accepts immutable attribution through lower-layer domain ports/snapshots supplied by upper consumers; preserve the existing inversion pattern and validate stale snapshots before paid dispatch. Domain has no Prisma dependency; DTO validation is presentation, persistence/mapping is data, and cross-slice imports use aliases/barrels. Per-slice Prisma sources and generated SDK contracts change only during implementation, never in this documentation task.
Expand, backfill, gate, contract
| Phase | Acceptance gate before proceeding |
|---|---|
| 1. Expand behind disabled flags | New org, external mapping, link, session, operation/outbox and payer-history tables; direct auth remains compatible; partner routes default deny |
| 2. Backfill | Existing User/Team/Agent IDs, owners, plan IDs, payments and subscriptions unchanged; assign existing teams to Agentfy; reconstruct payer attribution only from evidence, quarantine ambiguous history for review |
| 3. Auth and allocation | No-email actor end to end, two-proof links, endpoint opt-in, versioned lifecycle sync, transactional bootstrap and owner guards; all A scenarios below executable |
| 4. Billing | Both organization-paid and team-paid v1 paths, real provider consent/cutover/reconciliation, invoice retention, hard budgets and sponsorship accounting; no paid pilot before passing |
| 5. Pilot | Test namespace first, then limited production organization; cancellation/lease targets measured, sanitized serialization checked, EN/RU/UK UX and recovery exercised |
| 6. Contract | Only after old clients/tokens and rollback readers are retired; remove legacy mandatory-email assumptions and obsolete paths after compatibility evidence |
Separate flags govern partner provisioning, browser authorization/linking and paid billing. Disable issuance/paid dispatch on rollback; preserve actors, links, tombstones, ledger and assignments. Drain/reconcile committed outbox/provider effects idempotently. A rollback to binaries that cannot represent no-email actors is unsafe once such actors exist: retain compatible readers or roll forward. Never “roll back” by merging users, deleting history or restoring a reused external ID.
Numbered adversarial walkthrough
Each row is a design expectation and a future executable test, not a passing runtime test report.
| ID | Setup and action | Expected result | Enforcement point / future executable test |
|---|---|---|---|
| A01 | Two requests bootstrap same namespace/team with same key | One actor/team/owner/assignment/operation | Unique receipt + local transaction; concurrent HTTP integration test |
| A02 | Different keys or expired receipt target same external team | Same fingerprint resumes; changed payload conflicts, removed creator stays removed | Permanent mapping/fingerprint; rotate key, expire receipt and retry test |
| A03 | Crash after local commit before response/outbox completion | Recover same operation; no orphan charged team or duplicate agent | Durable outbox + step idempotency; crash-injection test |
| A04 | BONSITE key supplies another org/team/agent ID | Nondisclosing denial, no read/write | Whole-chain namespace authorization; routes/jobs/search/export isolation suite |
| A05 | Malicious identity issuer logs in as linked partner subject | Own-subject rights only; no direct/private/other-partner access or link metadata | Source-bound scoped principal; enumerate all protected routes and serializers |
| A06 | Provision key calls browser issuance or direct payment acceptance | Denied regardless of known subject/account ID | Separate credential scopes + independent financial proof; negative API tests |
| A07 | Two direct accounts confirm link to same external subject | Exactly one active link; loser learns no other account details | Subject lock + unique active link; simultaneous confirmation test |
| A08 | Same email in invite, direct account and partner profile | No automatic link, credential claim or invite retarget | Independent proof + fixed target; collision/forwarded-invite tests |
| A09 | Attacker sends unsolicited login callback/code; wrong browser or PKCE | No session and no account replacement | Agentfy transaction binding/state/nonce; login-CSRF and code-leak tests |
| A10 | Two tabs redeem same code; replay, expiry or cancellation | One winner; other flows fail without context mutation | Atomic consume + tab context; parallel redemption/browser tests |
| A11 | Unlink followed by stale JWT/refresh/socket/download use | Derived context refused; unrelated direct and valid partner-origin sessions unaffected | Link epoch + session-family revocation/proxy; refresh and streaming tests |
| A12 | Traverse D→P→another link or refresh partner into direct | No privilege union or source change | One-hop context resolver and immutable source; graph/token-confusion tests |
| A13 | Two owners concurrently demote/remove themselves | At least one active owner remains | Team lock/version; concurrent membership transaction test |
| A14 | Last external owner security-revoked while agents run | Revoke succeeds, team/jobs suspend, evidence retained; no arbitrary promotion | Subject lifecycle + owner/agent checks; live-job cancellation test |
| A15 | Remove actor owning agents, or transfer owner without payer consent | Explicit reassignment/suspension; payer unchanged | Membership FK plus transaction workflow; background-job/ownership test |
| A16 | Payer cutoff races with usage, late event and duplicate callback | Exactly one historical assignment per charge, no duplicate money/invoice | Timeline lock, occurrence attribution, dedup; boundary/reordered-event tests |
| A17 | Two jobs reserve remaining budget; one times out at provider | No overspend or premature reservation release | Atomic budget reservation + reconciliation; concurrent fault test |
| A18 | Sponsor revoked while payer proposal awaits acceptance | Paid work stops unless valid coverage remains; no automatic owner charge | Activation revalidation; pending-sponsor-removal test |
| A19 | Subject removed then stale activation arrives | Old/duplicate revision cannot reactivate or extend subject lease; restoration requires new grants | Monotonic lifecycle revision/tombstone; reordered webhook test |
| A20 | Team deleted then original bootstrap retried | Tombstone/unavailable result, never new team or live access | Permanent external claim + current-state check; deletion/retry test |
| A21 | BONSITE disappears or org closes while team is team-paid | Org work denied too; direct/private teams remain usable, no silent migration | Org status + lifecycle freshness; outage/closure test |
| A22 | Issuer key compromised; attacker prepared codes or links | Revoke pending proofs, quarantine affected links and sessions; direct credentials untouched | Credential/auth epoch + provenance; incident-time-range test |
| A23 | Old payer removed from team seeks historical bill | Invoice-only access remains, team data stays denied | Payer ACL separate from membership; invoice authorization test |
| A24 | Add direct credentials from partner context; first-user founder eligible | No extra team/agent, no founder promotion; failed link leaves no duplicate resources | Dedicated credential flow + founding exclusion; empty-install test |
| A25 | Delete direct account versus unlink versus deprovision partner | Distinct effects; preserve other actor/team/debt and no renewed revoked grants | Separate lifecycle commands; full lifecycle/relink test |
| A26 | Test key/worker token/guest token sent to production user routes | Denied; known IDs or valid signature do not widen authority | Environment + audience/type policy; token matrix test |
| A27 | Owner transfer accepted after membership or org policy changed | Stale proposal rejected without partial demotion | Fresh proof/version + active membership; accept/revoke race test |
| A28 | Current-state store unavailable or partner freshness lease expired | Fail closed; org heartbeat/stale event cannot renew subject lease | Authorization gate; outage/lease-expiry test |
| A29 | Partner owner invites their own direct account into a partner team | Refused; only same-namespace external customer actors, so deprovision cannot be bypassed | Membership target policy; direct-invite bypass test |
| A30 | Org-paid A30+B70, team-paid C20, internal sponsorship | Org invoice100, C20 separately, sponsorship not revenue; same direct payer does not merge team bills | Mode/team grouping key; mixed-payer invoice test |
Issues found and corrected during design
The earlier proposal left payer choice open and postponed team-paid behavior; both are now required in v1. Global-user linking would expose unrelated memberships; stable actors replace merges. A provisioning key is no longer also a browser-login credential. Claims that all organization operators are unable to read content were narrowed to reflect identity-issuer impersonation power. Direct invitations into partner customer teams are disallowed in v1 to prevent deprovision bypass. Last-owner protection now permits security suspension without orphan automation. Idempotency persists through rotation/receipt expiry without restoring deleted resources. Billing contact, ownership and accepted financial responsibility are separate transitions. Local allocation is atomic, but external provisioning readiness is asynchronous and honestly reported.
Additional corrections distinguish service initiator from owner, freeze the browser subject/code result, quarantine compromised-issuer links, renew leases per subject only from fresh reconciliation, and reserve billable sell-price rather than vendor cost.
The design uses explicit technical defaults for expiries, cutoffs and lifecycle leases. These are reviewable recommendations, not additional confirmed user decisions. Remaining implementation release gates are provider support for both payer modes, proven cancellation/lease timing, recovery verification procedure, retention/legal policy, and reviewed partner protocol interoperability. Failure of a gate blocks that capability; “later” must not enable an unsafe paid or login path. Cross-organization migration, privacy from the partner identity issuer and nested resellers remain outside v1; closure uses authorized export/support, not automatic transfer.
The walkthrough finds the documented invariants mutually consistent, subject to the release gates above. It is ready for design review, not a proof of security or permission to release runtime code.
Documentation validation
Validate VitePress build, local page/anchor links, navigation and EN/RU/UK section/scenario parity. Required project make check, app/admin builds and current HTTP startup checks are recorded in the PR report with actual outcomes. No API/app source is changed; there is no new runtime test result to claim. Reviewers should follow the three journeys—direct signup, external-only BONSITE user, existing direct account linking—and then trace A01–A30 against the contracts.