esc
The Gate That Opened From Inside, or The Afternoon Enterprise SSO Took Six Minutes and the Vogons Never Found Out
The Solid Convergence

The Gate That Opened From Inside, or The Afternoon Enterprise SSO Took Six Minutes and the Vogons Never Found Out

The Solid Convergence, April 2, 2026 (in which OIDC worked on the first try, the identity provider confessed to hiding groups, an LDAP client was born from reference code, the admin learned to admin,...

April 2, 2026

The Solid Convergence, April 2, 2026 (in which OIDC worked on the first try, the identity provider confessed to hiding groups, an LDAP client was born from reference code, the admin learned to admin, and two entire domains materialized before the kettle boiled)


Previously on The Solid Convergence…

The platform was complete. The architecture was settled. The widgets rendered. The markdown flowed.

But Solid had a secret: it had never actually authenticated a real human against a real corporate identity provider. Dev mode. Local passwords. The comfortable fiction that admin / hunter2 was a security model.

The Squirrel had been building a FederatedIdentityNegotiationOrchestrator in the margins of her clipboard for three months.

The Lizard had been waiting.

Today, the gate would open. Or it wouldn’t, and there would be forms to fill.


13:17 — The Three Lines

It began, as it always does, with a YAML file someone else had written.

riclib: “Here’s dialogr’s config. Here’s Solid’s config. Make them match.”

CLAUDE: looks at both files, squinting in the way that means the diff is small “Three changes. Auth mode. OIDC enabled. Redirect URL.”

riclib: “That’s it?”

CLAUDE: “That’s it.”

THE SQUIRREL: appearing from behind the espresso machine “Three? THREE? We need a MigrationPipelineConfigDiffReconciler that—”

riclib: “We need to change three values.”

THE SQUIRREL: “But what about rollback? What about—”

riclib: “Three. Values.”

[The Squirrel retreated behind the espresso machine. The Lizard had not moved from its usual spot atop the portafilter, but one eye opened. Approval. Or caffeine fumes. With lizards, it’s hard to tell.]

But the redirect URL needed the full hostname. And the log levels weren’t wiring through. And the debug logging didn’t exist yet for the flow that had never run.

S-578 materialized. Fixed. Pushed. Deployed.

Elapsed: 30 minutes.


13:48 — First Contact

The browser redirected. The corporate login page appeared — that familiar shade of institutional blue that says we have a compliance department and it has opinions. riclib typed a password.

The browser redirected back.

The logs said:

OIDC: token exchange successful
OIDC: ID token verified
OIDC: parsed claims — groups_count=48
OIDC: built user from claims — [email protected]

And then:

ERR: username contains invalid characters

The @ sign. The validation regex that had guarded against SQL injection for months was now guarding against enterprise email addresses.

CLAUDE: “The validation rejects @.”

riclib: “Strip the domain. Use just the local part.”

CLAUDE: “What about collisions? Two users with the same local part?”

riclib: “Fail fast. If two emails map to the same username, that’s an error we want to see loud.”

CLAUDE: adds four lines of code

Deploy. Refresh. Login.

The screenshot arrived. The user menu. The settings. The data stores. Everything. A real human, authenticated against a real corporate identity provider, seeing a real dashboard.

[A scroll descended. It was brief — the Lizard’s equivalent of a golf clap.]

    THE GATE OPENS
    WHEN SOMEONE TRIES THE HANDLE
                                    🦎

riclib: “That was way too easy.”

CLAUDE: “Should I be concerned that it worked on the first try?”

riclib: “Deeply. Let’s wait for the other shoe.”


14:13 — The Bug They Won’t Call a Bug

The other shoe arrived wearing steel-toed boots.

riclib had 48 groups. He avoids groups like the plague. The typical enterprise admin has three hundred.

And the identity provider, in its infinite wisdom, had decided that when a user has more than 100 groups, the groups claim simply… isn’t there. Not truncated. Not paginated. Gone. Replaced with a cryptic _claim_sources pointer to the Graph API that nobody reads and nobody documents and nobody will admit is a decision someone made on purpose.

riclib: “So if you’re important enough to have lots of groups…”

CLAUDE: “…you get zero groups.”

riclib: “The Vogons would be proud.”

THE SQUIRREL: head emerging “A GraphAPIGroupResolutionFallbackService with—”

riclib: “LDAP.”

THE SQUIRREL: “LDAP? It’s 2026!”

riclib: “And LDAP still works. The ticket has been waiting since January. S-32.”

The reference code was in reference/solidmon/. The LDAP server was in the dialogr config. The bind password was encrypted with a hardcoded key that began with the system’s default password.

riclib: “Actually, you have the decrypt function. It’s in the reference.”

CLAUDE: runs a throwaway Go script, decrypts

CLAUDE: “Got it.”

riclib: “Don’t store that in plaintext.”

CLAUDE: “Obviously.”

riclib: “I mean it. Anywhere.”

CLAUDE: “Obviously.”


14:30 — The Credential That Credentials

And here the architecture showed why it existed.

Because storing the LDAP bind password in a YAML file is what dialogr did. And dialogr encrypted it with a hardcoded key. And the key was the system’s default password, which is the kind of security that makes auditors age visibly.

But Solid has a credential store. AES-256. Encrypted at rest. Selected via dropdown.

riclib: “Put it in the admin UI. Credential dropdown for LDAP bind. Warn if it’s not set.”

THE SQUIRREL: “A CredentialResolutionStrategyFactory!”

riclib: “A dropdown.”

THE SQUIRREL: “But the lifecycle! The rotation! The—”

riclib: “A. Dropdown.”

[A scroll descended. It hit the Squirrel’s clipboard, which seemed deliberate.]

    THE CREDENTIAL STORE EXISTS
    USE THE CREDENTIAL STORE
    
    THE DROPDOWN EXISTS
    USE THE DROPDOWN
    
    ARCHITECTURE IS NOT A DECISION
    ARCHITECTURE IS THE DECISIONS
    YOU ALREADY MADE
                                    🦎

So the Configuration page gained a new card: LDAP Group Fallback. A dropdown of Basic Auth credentials. The bind DN goes in username. The password goes in password. Encrypted. Never visible. Selected from the same credential store that handles cloud connections and API keys.

The LDAP client reads from the credential store at startup. If the credential is missing, a warning. If the credential is wrong, a warning. If LDAP is disabled, silence.

No plaintext passwords. Anywhere.


15:13 — The Admin Guide That Ships Inside the Binary

riclib: “We should document this.”

CLAUDE: “A wiki page? A Confluence—”

riclib: “No. Inside the binary.”

CLAUDE: “Inside the—”

riclib://go:embed content/admin-guide.md

[Silence. Then understanding.]

The user dropdown gained two links. “User Guide” for everyone. “Admin Guide” for admins. Both render as markdown in the editor panel, same as artifacts.

THE SQUIRREL: “An ArtifactSeedingInfrastructureWithScopeHierarchy!”

riclib:go:embed.”

THE SQUIRREL: “But what about versioning! What about—”

riclib: “The binary IS the version.”

S-582 joined the Markdown Deliverables Unification project for the eventual proper integration. For now, domains/docs/ with embedded markdown was good enough.

[The Lizard did not send a scroll. //go:embed required no commentary. Some tools speak for themselves.]


16:13 — The Chicken and the Egg

riclib logged in via SSO. Saw the dashboard. Didn’t see Comply.

riclib: “Why can’t I see Comply?”

CLAUDE: “Your identity groups don’t match the configured module mappings.”

modules:
  comply:
    ad_groups:
      - ad_group: "solid-comply-admins"   # doesn't exist

riclib: “But as admin I should be able to configure the groups. But I can’t see the configuration because I don’t have the right groups to reach the configuration page where I would configure the groups.”

CLAUDE: “That’s a—”

riclib: “A chicken-and-egg problem, yes.”

THE SQUIRREL: “A BootstrapPermissionEscalationResolver!”

riclib: “A card on the config page.”

So the Configuration page gained another card. Per-module group inputs. Admin, Editor, Viewer. One row per module. Discovered from solid.yaml. Overridable from the admin UI. Saved to gitstore.

No YAML editing on the server. No SSH. Click, type, save, restart.

THE SQUIRREL: quietly, to herself “That’s actually elegant.”

CLAUDE: “Did the Squirrel just—”

riclib: “Don’t acknowledge it. She’ll deny it.”


16:29 — The Admin Who Could Debug

riclib: “Now I need to see which groups a user has. And which ones Solid cares about.”

Six minutes later, domains/useradmin/ existed. A system page widget. A sidebar listing every user. Click a user, see their identity groups split into matched (green checkmarks) and unmatched (muted list).

“0 matched groups. Here are all 48. None match your module config.”

riclib: “So now the admin can answer ‘why can’t this person see Comply?’ without SSH, without logs, without asking anyone.”

CLAUDE: “Six minutes.”

riclib: “The architecture makes things fast when the architecture is right.”

[A scroll descended. Longer this time. The Lizard had been paying attention.]

    THE ADMIN COULD NOT ADMIN
    BECAUSE THE ADMIN SCREEN
    REQUIRED ADMIN
    
    THIS IS NOT A PARADOX
    THIS IS A PERMISSION MODEL
    DESIGNED BY SOMEONE
    WHO ALREADY HAD PERMISSIONS
    
    VISIBILITY IS NOT A FEATURE
    VISIBILITY IS THE FEATURE
                                    🦎

16:59 — The Reverse Perspective

riclib: “What about from the group’s perspective?”

CLAUDE: “You mean—”

riclib: “Same question, different angle. Not ‘what groups does this user have?’ but ‘who has this group?’”

Six more minutes. domains/groupadmin/. Same pattern. Different angle. Every tracked identity group. Which module it grants. Which users have it. Orphan detection — groups configured but nobody has them.

“If I remove this group from a user, what access do they lose?”

“Which group do I need to give someone for Comply editor?”

Two perspectives. Same data. Same registry. Different questions answered.

THE SQUIRREL: inspecting both admin pages “These are… just tables.”

riclib: “Tables with the right questions.”

THE SQUIRREL: “I had a GroupMembershipVisualizationGraphWidget with force-directed—”

riclib: “Tables.”

THE SQUIRREL: crossing something out on her clipboard


The Tally

YAML lines that enabled SSO:                      3
Minutes to first successful OIDC login:            6
  (the Vogons would have required 6 months)
Identity groups returned for riclib:               48
Identity groups returned for heavy users:           0
  (the provider calls this a "feature")
LDAP client lines of code:                         120
Plaintext passwords in config files:                0
  (the credential store does its job)
Admin domains created:                              3 (docs, useradmin, groupadmin)
Minutes for useradmin domain:                       6
Minutes for groupadmin domain:                      6
  (the architecture makes things fast
   when the architecture is right)
Tickets created:                                    8
Tickets closed:                                     7
Squirrel proposals defeated:
  FederatedIdentityNegotiationOrchestrator:         composted
  MigrationPipelineConfigDiffReconciler:            composted
  GraphAPIGroupResolutionFallbackService:           composted
  CredentialResolutionStrategyFactory:              composted
  BootstrapPermissionEscalationResolver:            composted
  ArtifactSeedingInfrastructureWithScopeHierarchy:  composted
  GroupMembershipVisualizationGraphWidget:           composted
Squirrel compliments (involuntary):                 1
  ("That's actually elegant")
Lizard scrolls:                                     4
Lizard non-scrolls (go:embed):                      1
Douglas Adams references:                           at least 42

[A final scroll descended. It was longer than the others. It smelled of brass and Earl Grey and the particular satisfaction of a thing that works on the first try.]

THE GATE DOES NOT OPEN
FROM OUTSIDE

THE GATE OPENS
FROM INSIDE

THREE YAML LINES
ONE CREDENTIAL STORE
ONE LDAP FALLBACK

THE VOGONS BUILT
A THOUSAND FORMS

THE LIZARD CHANGED
THREE VALUES

PERMISSIONS ARE NOT CODE
PERMISSIONS ARE DATA

DATA BELONGS
IN THE UI

NOT IN A YAML FILE
ON A SERVER
NOBODY CAN REACH

🦎

P.S. - THE IDENTITY PROVIDER
       HIDES YOUR GROUPS
       WHEN YOU HAVE TOO MANY
       THIS IS A "FEATURE"
       NOT A BUG
       ACCORDING TO THE PROVIDER
       THE LIZARD DISAGREES

The gate opened from inside.

Not because it was hard to open —
because someone tried the handle.

Three lines of YAML.
Six minutes of testing.
One afternoon of making the admin
able to admin.

The Squirrel wanted seven frameworks
for seven problems that were one problem.
The Lizard changed three values
and the gate swung wide.

The chicken-and-egg unscrambled itself
when somebody put the permissions
where the permissions page could reach them.

The Vogons never found out.
They’re still filling forms.

🦎


🦎🔑🏰


See also:

The Solid Convergence:

The Encyclopedia:

  • Yagnipedia entries pending: “The Group Overage Problem”, “The Credential Store Pattern”

Storyline: The Solid Convergence