Bragi, skaldic god of poetry, keeper of every tale worth telling.
Image credit: @norsemythologyclips — go follow them.
Bragi is the story: Norse.DesignSystem.Stories — a Razor Class Library of .stories.razor catalog pages and Markdown documentation (via MD2RazorGenerator) for the platform's Blazor components. Bragi doesn't build the ship; he sings of everything aboard it.
Bragi ships no runnable app of its own. Yggdrasil hosts the runnable BlazingStory catalog built from it (Hosting.Stories, Blazor Interactive Server), published as a container to ghcr.io/norsearchitecture/hosting/stories — the BlazingStory package reference here is purely the .stories.razor authoring API, a content-authoring dependency, not a hosting one.
Stories are organized by surface, not by file. Each entry in the sidebar is a component pinned in one named state, grouped under the flow it belongs to — a confirmation page isn't its own sidebar entry, it's the succeeded state of the surface that owns it ("Forgot Password → Email Sent", never "ForgotPasswordConfirmation").
- Authentication (over Heimdall's
AuthN.Components.FluentUI): Login — Default · Validation Errors · Invalid Credentials · Locked Out · Not Allowed · External Providers · Two-Factor Required; Login With Passkey — Default · Fault; Register — Default · Validation Errors · Email Taken · Invalid Password; Two-Factor — Default · Validation Errors · Invalid Code · Locked Out · Expired Challenge · Lockout Page; Recovery Code Sign-In — Default · Validation Errors · Invalid Code · Expired Challenge; Forgot Password — Default · Validation Errors · Email Sent · Fault; Reset Password — Default · Validation Errors · Invalid Link · Password Policy · Invalid Token · Password Reset; Confirm Email — Confirm · Confirmed · Failed · Invalid Link; Confirm Email Change — Confirm · Failed; Resend Email Confirmation — Default · Validation Errors · Sent; Access Denied — Default; Recovery Codes — Default - Account (over Heimdall's Manage pages, inside its
ManageLayout): Profile — Default · Validation Errors · Saved · Fault; Email — Default · Unconfirmed · Validation Errors · Link Sent · Fault; Change Password — Default · Validation Errors · Changed · Incorrect Password · Password Policy · Fault; Set Password — Default · Set · Password Policy · Fault; External Logins — Default · Removed · External Only · Fault; Delete Personal Data — Default · Passwordless · Deleted · Incorrect Password · Fault; Personal Data — Default · Download; Two-Factor Authentication — Default · Two-Factor Disabled · Recovery Codes Low · Forget This Browser · Fault; Enable Authenticator — Default · Verified · Invalid Code · Fault; Recovery Codes Handoff — Default · Revealed; Reset Authenticator — Default · Reset · Two-Factor Disabled · Fault; Disable 2FA — Default · Disabled · Not Enabled · Fault; Generate Recovery Codes — Default · Generated · Two-Factor Disabled · Fault; Passkeys — Default · No Passkeys · Fault; Rename Passkey — Default · Fault - Primitives (reusable widgets, over Asgard's
Abstractions.Components): Loader — Default · Custom Label; StatusMessage — Success · Error; ModelValidationSummary — Model Errors (harnessed inside a story-only form, since it can't render outside one)
Reference.Components.FluentUI (Mímir) stories remain future work once that realm's components ship.
If you're a designer landing here, this is the part that won't be obvious, and it's the part that makes the catalog worth your time.
The components in the catalog are the real production components. Not mockups, not copies kept in sync by hand — the same Login and Register that ship, with the same markup, the same validation rules, the same error rendering. When one of them changes in its home repo, the catalog changes with it.
But nothing real is running behind them. In production, submitting Login calls an actual authentication service and actually signs someone in. The catalog has no servers, no database, no accounts — it's a static site running entirely in your browser. So how does an interactive Login story work at all?
The trick is that these components never talk to a service directly — they talk to an interface, a contract that says "something that can log people in stands here." At startup, the app decides what that something is. The production app plugs in the real service. The catalog plugs in FakeAuthenticationService — a small scripted stand-in that answers instantly, in-browser, with responses shaped exactly like the real service's. The component can't tell the difference, and that's the point: the actors on stage are real; only the world backstage is scripted.
What that buys you: every state a component can be in becomes triggerable on demand, with zero setup. Type fail@example.com into the Login story's own email field and the real component renders the real invalid-credentials failure, drawn exactly as production would draw it — no test account, no server, no engineer on call. You're styling the true component in the true state, with none of the machinery.
That typing trick is a playground garnish, though, not how the rest of the catalog works. Every other named state you see — Locked Out, Invalid Password, Email Taken, and the rest — is pinned deterministically: the story itself tells the fake which state to answer with, so the page renders identically, as its own bookmarkable URL, every time anyone loads it. Nothing accumulates and nothing depends on what you clicked first. Every state and its trigger is written up on the catalog's own Scenarios page. The account pages work the same way: FakeAccountService and FakeIdentityService serve one made-up account (designer@example.com), FakeAuthenticationStateProvider says who is signed in, and StoryShellLayout stands in for the real app's frame, so the account pages show their own layout and nothing else. The two-factor and passkey pages add FakeTwoFactorService and FakePasskeyService, and the sign-in pages' links come from FakeSignInMethodsService, each answering in the real server's shape. A state that only exists after a click or a submit is played on load by the story itself, the way a Storybook play function would. Since Train 3 (T3-7) a click may leave the page exactly as it was, which is what a suppressed sign-in refresh or a saved file looks like, so External Logins → Removed and Personal Data → Download are back in the catalog. The plumbing itself stays out of sight: the host wires the fakes with a single call (AddNorseStoryFakes()) and knows nothing else about them.
dotnet build Bragi.slnx # warnings are errors — a single warning fails
dotnet test Bragi.slnx # xUnit v3 + Shouldly on Microsoft.Testing.PlatformRequires the .NET 11 preview SDK pinned by global.json. The realm builds standalone — it is its own clone target, not only a Bifröst submodule.
Split out of Naglfar on 2026-07-12, the same day DesignSystem.Stories first landed there as a plain RCL — see the hosting design (superseded in part) for the original decision and its addendum recording the split. Naglfar keeps the token pipeline; Bragi owns the story content — two different publish cadences (npm vs. NuGet) that don't belong wearing one repo's clothes.
Bragi rides as a submodule of Bifröst, the Norse Architecture's meta-repository, alongside every other realm. Story content here — stories, story authoring, what a component's catalog page shows — is exempt from the platform's brainstorm → spec → plan → TDD cycle, the same standing call as Naglfar. The one behavioral exception is the fake behind the stories: its design session ran and shipped 2026-08-08, and that piece rode the full discipline — brainstorm, spec, plan, TDD — like any other realm's code.

