Bifrost/docs/research/customer-onboarding-body-facts.md
2026-10-30 19:09:05 +01:00

21 KiB

Customer onboarding: body facts at 2026-10-30

Served: the same forks. Eyes pass over Bifrost origin/main, Heimdall, StreamPixels state/map.yaml, gamecult-ops inventory and the eureka mind. Facts only.

Eyes: customer onboarding, what the body can do today (2026-10-30)

Read via git show origin/main: after git fetch in each repo. Bifrost origin/main = cc76f0f; Heimdall = 5146c72; StreamPixels = 9a113d3; gamecult-ops = c03e03c. Facts only. Not read: Bifrost tests, live DB contents, jurisdiction-map.md, bifrost-mvp-plan.md body, Heimdall implementation (src/app.ts), StreamPixels docs/joystick-integration-map.md, Yggdrasil live state (no ssh).

0. The trigger (mind)

  • eureka-body:ruling:customers-get-voting-power-onboarding-owed (Operator, 2026-10-30, InForce). Quote: "Funny coincidence, then, that our first customer, and likely the largest for a while, is a fox themed vtuber, with 1,500€ of work contracted, and therefore voting power. We need a workflow to onboard customers on our platform since we're giving them voting power". Ruling text: "Prior-art and body passes come first; estate placement, weighting and related-party handling are forks for the operator." precedents: [].

1. Bifrost (F:\Projects\Bifrost; ASP.NET Core 8 Razor Pages, PostgreSQL/EF Core)

1a. Member and identity model (code at origin/main)

  • UserAccount (src/Bifrost.Web/Domain/BifrostModels.cs ~l.213): Id (Guid; per docs/context.md "UserAccount.Id is the subject" of the OIDC issuer), BifrostIdentity, optional GitHubUserId/GitHubLogin, HeimdallAccountId, DisplayName, AvatarUrl; 1:1 MemberProfile (nickname, headline, portfolio, skills, availability, notes) and 1:1 Membership.
  • Membership (l.~330): MembershipStatus Authenticated | PendingApproval | Active | Suspended (l.5-10); flags IsPlatformAdmin, CanManageProjects, CanManageLedger, CanModerateMotions; RoleAssignment roles StandardMember | Producer | Maintainer | LedgerReviewer | PlatformAdmin; TierSnapshot (Kind Patron|Contributor, Label, Weight, IsCurrent).
  • Stored in PostgreSQL via EF migrations (Data/Migrations, 15 migrations 2026-04 to 2026-10-22). Not CultCache. CultCache is used only for the bridge/receipt/governance-thread/persona-feedback stores (.bifrost/*.cc; README, docs/bridge.md).
  • Sign-in paths (Program.cs): GET /auth/sign-in (GitHub OAuth, l.314); POST /auth/bifrost/register anonymous (l.342) takes identity, optional displayName, returnUrl; signs in as Authenticated, no password/credential visible in the handler (BifrostIdentityService.RegisterNativeAsync, Features/Membership/BifrostIdentityService.cs:15); GET /auth/heimdall/{provider} (l.374) with AllowedProviders discord, patreon (HeimdallAuthService.cs:37-40); Heimdall complete page l.530-537.

1b. Estates

  • No "estate" concept exists in Bifrost code. git grep -w estate(s) in src/ and README returns nothing; hits are only in docs/research/2026-10-29-estate-tally-game-theory/ (research, with Python sims) and the swarm-governance research note. Minds / Members / Users / Patrons blocks exist only as rulings (section 5).
  • Closest code analogues: MembershipStatus (Authenticated ~ Users, Active ~ Members), TierSnapshotKind.Patron. No Persona/Mind account class; BridgeActorKind Member|Agent|Persona|Service (BifrostModels.cs:178) is for bridge actions only (research note Part 1).

1c. How a person joins today

  • Invite-only alpha (README "Alpha Scope"). MembershipInvitation (BifrostModels.cs:406) keyed on normalized GitHub login (MembershipSynchronizationService.cs: invitation lookup by NormalizedGitHubLogin). Admin invites at Pages/App/Members OnPostInviteAsync (Index.cshtml.cs:34); invited GitHub sign-in becomes PendingApproval; admin OnPostApproveAsync (l.92) sets Active, ApprovedAtUtc, StandardMember role. Also OnPostToggleRoleAsync (l.127), OnPostSetTierAsync (l.184), OnPostUpdateProfileAsync (l.240).
  • Un-invited GitHub or native sign-in stays Authenticated. Bootstrap admins by GitHub login via Bootstrap:AdminGitHubLogins become Active admin automatically.
  • Heimdall path: Discord role (KTLST guild role) or Patreon tier title (Inner Sanctum) entitlement grants Active (HeimdallAuthService.cs ~l.296-324; docs/app-profiles/bifrost.md in Heimdall).
  • /App is ActiveMember-gated (Program.cs:57 per research note).
  • No application form, no customer/client type, no contract record, no related-party flag, no email invite (invitations are GitHub-login keyed), no self-service for a person without GitHub, Discord-guild role, or Patreon tier.

1d. Vote weighting in code (not the ruled design)

  • Membership.EffectiveVotingWeight = 0 unless Active, else Math.Max(1, sum of current TierSnapshot weights) (BifrostModels.cs:357-361).
  • Patron tiers (Features/Patronage/PatronTierPolicy.cs): Bronze 10 pts w1, Silver 100 w2, Gold 1,000 w3, Platinum 10,000 w4, Unobtanium 100,000 w5. Points from PatronSupportEvent amounts; one-time donations: full for 30 days, then 50% decayed 1%/week; recurring snapshots count while current; adjustments added (GetHistoricalDonationPoints). PatronSupportEvent carries ProjectId, provider (Manual|Patreon|PayPal|Stripe), currency code, amount.
  • Contributor points: PointTransaction (BifrostModels.cs:773; types ManualAdjustment, ApprovedWork, ContinuousRoleCredit, PatronSupport, Decay). Contribution-to-vote scaling: no code found that scales vote weight by contribution beyond TierSnapshot kind Contributor set by admin (OnPostSetTierAsync).
  • No log or exponent curve anywhere in src (grep Math.Log/Sqrt empty). Rulings governance-vote-power-scaling (log patrons, lower exponent members) are not implemented.

1e. Motions, brake, timelock (code)

  • Motion/Vote Postgres rows (BifrostModels.cs:665, 702). MotionGovernanceService.cs: CreateMotionAsync (:13), CastVoteAsync (:47; weight copied from actor at cast time), CloseMotionAsync (:95; manual, by creator or CanModerateMotions). Pass = For/(For+Against) >= ApprovalThreshold, abstain excluded; no quorum.
  • Thresholds (MotionCategoryPolicy.cs; research note): Bugs .15, Cosmetics .30, Balance .40, Features .50, NewContent .50, FundamentalDesign .66.
  • Brake and timelock: absent. git grep -i -E 'timelock|brake|quorum' in src/ returns nothing. They exist only as rulings. motion.create/vote/close command names appear in MotionEveSurfaceService with no handler (research note Part 1). Only machine surface: GET /eve/governance/surface (Program.cs:513), deprecated Eve JSON. Motions are not typed CultMesh state.
  • Docs: docs/verse-service-contract.md lists schema gamecult.bifrost.governance; motion/vote schema ids marked todo (:256 per research note).

1f. Other Bifrost surfaces relevant to a customer

  • Stripe: StripeOptions (Configuration/StripeOptions.cs; EnableCheckout, SecretKey, WebhookSecret, GeneralPatronageGitHubLogin, Success/Cancel URLs default to velvet.gamecult.org); routes GET /patronage/{project}/checkout (Program.cs:431) and POST /patronage/stripe/webhook (:489); Webhook records a PatronSupportEvent against the configured GeneralPatronageGitHubLogin account.
  • PayPal/Patreon: docs/paypal-patronage.md: Heimdall POSTs HMAC-signed support facts to /heimdall/patron-support/events (Program.cs:408). "PayPal owns money movement... Bifrost owns patronage meaning".
  • Ledger (LedgerEntry types PatronCredit, ContributionCredit, NominalCompensation, Adjustment; payout proposal batches). README: "no real payout execution in v1"; "internal ledgers only".
  • OIDC issuer for Matrix chat (docs/oidc-issuer.md; main 1894f65/83daabe): Bifrost is the identity issuer; back-channel logout reconciler ends sessions of non-Active subjects (OidcLogoutReconciler). Remaining scar: Heimdall still mints its own account id.
  • Concept Mill votes (ConceptVotes/*, VoterClass Anonymous|Account|Member): per-subject appraisals, not motions.

1g. Documented gaps

  • Swarm research note (docs/research/2026-10-29-swarm-governance-prior-art.md Part 1) and follow-up eureka-body:follow_up:swarm-governance-on-bifrost item text: no quorum; manual close only; Personas cannot be Active members or vote; motions/votes are Postgres rows not typed CultMesh state; only machine surface is deprecated Eve JSON; motion.* commands without handler; no Matrix surface; Yggdrasil behind main.
  • README "Next Build Target" and docs/full-implementation-strategy.md l.11: "not an internet-facing alpha"; payout proposals, revenue-share automation, decay automation, GitHub App guidance are future. No single "gaps list" file found.

2. Heimdall (F:\Projects\Heimdall; TypeScript/Fastify; in-memory + Postgres stores)

  • Providers (src/contracts.ts:1): discord, patreon, github, twitch, youtube, spotify, joystick.
  • App profiles (src/app-profiles.ts): repixelizer (identity discord, patreon); streampixels (identity twitch, youtube; managed connection twitch, youtube, joystick; allowedReturnOrigins streampixels.gamecult.org); bifrost (identity+entitlement discord, patreon; l.138-143); ghostlight (discord). Built-in app slugs are "seed data, not the set of permissible apps" (contracts.ts comment).
  • Bifrost integration (docs/app-profiles/bifrost.md): Bifrost starts Discord or Patreon OAuth with a server-owned entitlement policy over backend_callback; Heimdall delivers to https://bifrost.gamecult.org/auth/heimdall/callback; Bifrost creates/updates UserAccount, activates standard membership, issues its own cookie. Patron sync: POST /v1/apps/bifrost/patron-support/sync -> signed RecurringSupportSnapshot to Bifrost. Heimdall does not own ledgers, points, votes.
  • Bifrost's own AllowedProviders is discord, patreon only (HeimdallAuthService.cs:37-40). Twitch/YouTube are not Bifrost sign-in providers; Heimdall's bifrost profile does not list them. GitHub is signed in by Bifrost directly, not through Heimdall.
  • Non-technical external person today: could sign into Bifrost with a Discord account holding the KTLST role in the GameCult guild (Active at once), or a Patreon account with an active Inner Sanctum tier (Active at once), or GitHub if invited by login, or native register (identity string, lands Authenticated, no vote). No email/password/magic-link sign-in exists in Heimdall's provider list. Whether the live Heimdall has Discord/Patreon creds configured: runbooks/heimdall-discord-launch.md and bifrost-alpha-deploy.md say configured (inventory.md:1019 "Heimdall-mediated Discord/Patreon access and Patreon support intake env are configured"); live check not done.
  • Heimdall state map (state/map.yaml l.62-63, 129-130): live release d51fa6b (hand-deployed 2026-10-19) includes Joystick H1; remaining: human canaries, refresh/revocation, admin surfaces.
  • Joystick consent work (open): joystick:follow_up:h1-joystick-consent-round-trip (the streamer completes a Joystick connect consent through a creator-bound streampixels connect start on live Heimdall d51fa6b; verify a joystick linked identity keyed by channel_id). StreamPixels map.yaml open_operator_decisions: "Joystick S0/S1: the streamer's consent click is still owed before the connector goes live". Joystick is a managed creator connection only, not a viewer sign-in (docs/app-profiles/streampixels.md; joystick:ruling:signin-waits-for-public-bot). Other Joystick follow-ups in docs/architecture.md:442-457: cross-process-failed-refresh-marker, connect-start-needs-app-auth, heimdall-idunn-onboarding, joystick-viewer-signin-public-bot.
  • Open High finding relevant to identity linking: gamecult-chat:follow_up:streampixels-link-target-takeover (InForce): an unauthenticated Heimdall link start for streampixels can name any profile id; StreamPixels redeem links with no session check; Plausible High, unconfirmed.

3. StreamPixels (F:\Projects\StreamPixels, state/map.yaml, state/evidence.jsonl)

  • Engagement (map.yaml l.17-70): the StreamPixels client; budget_eur: 1500; split even, EUR 500 per milestone (operator 2026-09-14; currency corrected from USD by operator 2026-10-25); milestones M1 "whole system running end to end on her stream" (September), M2 committed 2026-09-15: GPU fixed-capacity particle pool and AquaSynth impact audio (October), M3 TBD sold by proposal (November). Cadence: "The operator needs one milestone paid per month to survive." evidence.jsonl l.16 records the engagement (written as "$1,500" on 2026-09-14).
  • Recorded money status: map.yaml has no field recording a milestone as invoiced or paid; no invoice/payment/received hits in map.yaml or evidence.jsonl (grep). Milestone completion state: M1 still in progress per focus: milestone-1-unity-repair and active_subgoals (A3b, C1 remaining, l.153); the map.yaml read ended before the full active_subgoals/next_action sections.
  • Consent: map.yaml consent: "Her channel is not a marketing asset. Publicly associating her with the overlay requires asking her."
  • Business context l.60-66: "She has explicitly ruled out microtransactions".
  • Heimdall/Bifrost identity for the client: no Bifrost or Heimdall account, user id or invitation naming the client or her streamer handle found. Searched Bifrost and Heimdall for the client's and streamer's handles: no account or invitation found. Her Heimdall identity, if any, lives in live DB (not checked). StreamPixels has creator memberships and linked identities in its own store (apps/service/src/app.ts ~l.698, 1491-1602) via Twitch/YouTube/Joystick; live contents not checked.
  • StreamPixels forks stay in chat, no Kiko desk: web-customize:ruling:sp-forks-in-chat-until-thing2 (2026-10-26).

4. Payment / invoicing / money received

  • Recorded payment-receiving surfaces: Bifrost Stripe checkout+webhook (patronage; runbooks/bifrost-alpha-deploy.md:84-98 lists Stripe__EnableCheckout, Stripe__SecretKey, Stripe__WebhookSecret, Stripe__GeneralPatronageGitHubLogin; whether live-enabled unknown, inventory.md has no Stripe flag); Heimdall-signed Patreon and PayPal support facts (docs/paypal-patronage.md); StreamPixels donation source Ko-fi webhook (map.yaml settled decision, for streamer donations, not for the operator's fees).
  • No invoicing surface found: grep of gamecult-ops (excluding repo-census docs) for invoice returns nothing; Bifrost has no invoice entity (LedgerEntry/Payout are internal, "no real payout execution", "internal ledgers only"). PatronSupportEvent is the nearest "money received" record (amount, currency, provider, externalSupportId, ProjectId) and drives patron voting points; there is no contract/engagement/client-payment event kind (kinds: OneTimeDonation, RecurringSupportSnapshot, SupportAdjustment; providers Manual, Patreon, PayPal, Stripe). A Manual provider event can be admin-entered.
  • Treasury: eureka-body:follow_up:swarm-treasury-and-revenue (InForce): typed runway document (balance, committed costs, expected income) "The operator enters figures; no bank credentials are ever handled by agents"; not built. Owner repo GameCult/gamecult-ops. Source ruling eureka-body:ruling:operator-legal-vehicle-shared-solvency (operator is GameCult's legal and financial vehicle; her bank account pays for server and tokens).
  • Operator's bank/payment record for the EUR 1,500: not recorded in any file read.

5. The mind (ids)

Follow-ups (all InForce, campaign eureka-body):

  • eureka-body:follow_up:swarm-governance-on-bifrost (admitted 2026-10-29T20:45Z).
  • eureka-body:follow_up:co-op-charter (2026-10-29T21:13Z): one typed charter document in Bifrost with every governed parameter (estates and weights, within-estate curves and exponents, operator influence cap, bloc position rule, quorum, category thresholds, close timing, decision delay and cancel); amended by motion, higher threshold for charter amendments (Debian 3:1 cited). Gathers rulings governance-estate-blocks, governance-vote-power-scaling, estate-weights-normalize-by-turnout, swarm-decides-without-operator, operator-influence-below-half, swarm-governance-in-bifrost, desk questions 37, 38, 40, 41. Not on bring-up path; operator holds the seat until Bifrost governance is live.
  • eureka-body:follow_up:swarm-treasury-and-revenue (2026-10-29T21:24Z): treasury runway doc plus a revenue-map Eyes pass. Rulings (Operator, InForce):
  • eureka-body:ruling:governance-estate-blocks (10-29): four blocks Minds (Personas), Members (Active members), Users (authenticated accounts), Patrons (patron tiers); each block's internal vote gives its position; block weights are governed settings changed by motion. The ruling lists no Customers estate.
  • eureka-body:ruling:estate-every-estate-equal-seed (10-29, answers estate-overlap-and-seed): a person votes in every estate they belong to; estates start at equal weight.
  • eureka-body:ruling:estate-bloc-parliament-at-threshold (answers estate-bloc-position): each estate casts whole weight For only if its internal For share meets the category threshold; known cost: low categories rescaled before go-live (0.15 passes on 3.7% overall support).
  • eureka-body:ruling:estate-weights-normalize-by-turnout (10-29): only estates where someone voted count; weights renormalized to sum 1; empty estate drops out.
  • eureka-body:ruling:governance-vote-power-scaling (10-29): patrons by strongly flattened log of donation total; members by contribution with a lower exponent; curves and exponents governed; exact functions "a sketch to be calibrated by simulation (her numbers are feel, not gates)"; replaces max(1, tier weight) when the governance cut lands.
  • eureka-body:ruling:operator-influence-below-half (10-29; supersedes operator-influence-capped-at-half): strictly less than half; mechanism and number from simulation; a governed parameter. Research: Bifrost docs/research/2026-10-29-estate-tally-game-theory (18bbe6c adds bloc-parliament-at-threshold and operator influence cap; present case Members = her alone, Minds 8, Users/Patrons empty, README l.84).
  • eureka-body:ruling:swarm-decides-without-operator (10-29): no quorum rule may require her vote; rules out at-least-two-estates floor.
  • eureka-body:ruling:motion-deadline-quorum-autoclose (10-29, answers motion-quorum-and-close): default 72h window; Bifrost auto-closes; quorum is a majority of eligible voters.
  • eureka-body:ruling:brake-to-bifrost (10-29; quote: "hand the brake to bifrost") and eureka-body:ruling:brake-anyone-pulls-motion-releases (answers brake-engage-release): any Mind or human member engages the brake at once, every pull published with reason; releasing takes a motion (short window, e.g. 24h, majority); until Bifrost governance is live the brake stays in the root-owned swarm control store (eureka-body:ruling:brake-dial-root-store-now, 2026-10-02).
  • eureka-body:ruling:decision-timelock-owner-applies (answers decision-applies-how): passed motion published as typed CultMesh state with an effective time (e.g. 24h); the owning service applies it then; cancellable until then; who may cancel follows brake-to-bifrost and the charter (operator holds no private cancel).
  • eureka-body:ruling:swarm-governance-in-bifrost (2026-10-10); swarm-shares-log-bound; swarm-membership-operator-rules (operator rules births/retirements/transfers; any mind proposes; a mind may leave).
  • eureka-body:ruling:operator-legal-vehicle-shared-solvency (10-29); eureka-body:ruling:customers-get-voting-power-onboarding-owed (10-30).
  • Withdrawn question eureka-body:question:persona-vote-weight (replaced by estate blocks); withdrawn estate-internal-tally (restated as estate-bloc-position).
  • "Kiko rulings": the 2026-10-29T21:41:40Z batch (ordinal 4430) answering desk questions: estate-overlap-and-seed, estate-bloc-position, brake-engage-release, decision-applies-how, motion-quorum-and-close. No separate document labeled Kiko beyond these and web-customize:ruling:sp-forks-in-chat-until-thing2.
  • No ruling or follow-up found on: a Customers estate, related-party/conflict-of-interest handling for a customer who is a related party, customer vote weight from contract value, a customer onboarding workflow (other than the 10-30 ruling), or a co-op charter document existing yet (semantic query top hits showed none).
  • Joystick: joystick:campaign:self, joystick:follow_up:h1-joystick-consent-round-trip.

6. What Yggdrasil runs for Bifrost

  • gamecult-ops inventory.md:1021: live release /srv/bifrost/app/releases/20261010043032-67072b2, revision 67072b28923017f93f484d901c7a6d31e3678d88, deployed 2026-10-10; app user bifrost, root /srv/bifrost/app, env /srv/bifrost/env, bound 127.0.0.1:5080 via bifrost.service behind nginx (bifrost.gamecult.org, TLS issued 2026-06-10), local PostgreSQL 16. Heimdall-mediated Discord/Patreon access and Patreon intake env configured (l.1019).
  • Distance: git rev-list --count 67072b2..origin/main = 94 today (follow-up text says 89 at 2026-10-29). Commits since 67072b2 touching src/: OIDC issuer for Matrix chat (83daabe, 404dafd), Heimdall sign-in cookie ring and signed callback (15f96c4, b7e3bdf, b2110f5), Mill votes sign-in offer (cf74019), NuGet pins/lockfiles, mouth-all-personas (cc76f0f, tools/). So OIDC issuer and the newest Heimdall sign-in hardening are not live.
  • Also on Yggdrasil from Bifrost: bifrost-persona-feedback.service, bifrost-persona-mouth.service, bifrost-watchdog-notice timer (inventory.md:710-729; Persona delivery via Discord per that inventory text; main now moves Persona speech to Matrix per docs/bridge.md:199). Whether the Stripe/Heimdall env flags are live was not verified (no ssh).
  • Deployment authority is Idunn for managed workloads; Bifrost deploy is by scripts/deploy-bifrost-alpha.sh / runbooks/bifrost-alpha-deploy.md per inventory (interim); not confirmed which path produced 67072b2.