2026-10-04 12:24:13 +02:00
<!doctype html>
< html lang = "en" data-theme = "tokyo" data-mode = "dark" >
< head >
< meta charset = "utf-8" >
< meta name = "viewport" content = "width=device-width,initial-scale=1" >
< meta name = "color-scheme" content = "dark light" >
< title > Go Loose · Profile< / title >
< link rel = "stylesheet" href = "/assets/style.css" >
feat: account menu, database roles, and single logout
Bundles the console work from the current branch: the header account
control, database-backed roles and permissions, and signing out across
the applications that share this sign-in.
The account control replaces a separate identity chip and a separate
sign-out button, which meant the header carried two controls describing
one fact. web/account.js is mounted into every page by a placeholder and
owns the keyboard handling, the outside-click behaviour and the sign-out
form once. It also fills the trigger's name, which the previous markup
left empty.
Signing out revokes the hosted-login bearer sessions this installation
issued, then redirects to the provider's end_session_endpoint when the
discovery document advertises one. Applications that authenticate
against this installation therefore lose their session too; one pointed
straight at an identity provider is out of its reach and is unchanged.
Roles and permissions become rows rather than a closed enum, so a tenant
can extend the catalog without a migration, and a role can no longer be
granted a capability that is not in it. requireSameOrigin now compares
against the origin the request arrived on instead of the configured base
URL, which defaulted to http://localhost:8080 and so rejected every
write in a deployment that never set it.
Two bugs found while verifying in a browser:
- A profile picture never rendered. avatarURL built an absolute URL
from the configured base URL, and the page's own img-src 'self'
blocked it. The console's own pages now take a root-relative path;
only /connect/userinfo, read from another origin, needs an absolute
one, and it follows the request.
- absoluteURL preferred the configuration over the request while its
comment claimed the opposite, which also misdirected the logout
redirect.
Card layout: the footer was one two-column row shared between a label
and a forty-character identifier, so the identifier took the width and
crushed its label into three stacked words. Each pair is a line now, the
value truncates with the full text in the title, and cards align their
footers across a row.
2026-10-05 19:18:07 +02:00
< link rel = "stylesheet" href = "__GO_LOOSE_THEME_ORIGIN__/rt/v1/contract.css" data-theme-contract >
2026-10-04 12:24:13 +02:00
< / head >
< body class = "profile-page" >
< div class = "noise" > < / div >
< header >
< a class = "brand" href = "/" > < span class = "brand-mark" > GL< / span > < span > Go Loose< / span > < / a >
< nav >
< a href = "/" > Console< / a >
< a href = "/docs" > API docs< / a >
2026-10-04 16:57:30 +02:00
< a href = "/roles" > Roles< / a >
feat: account menu, database roles, and single logout
Bundles the console work from the current branch: the header account
control, database-backed roles and permissions, and signing out across
the applications that share this sign-in.
The account control replaces a separate identity chip and a separate
sign-out button, which meant the header carried two controls describing
one fact. web/account.js is mounted into every page by a placeholder and
owns the keyboard handling, the outside-click behaviour and the sign-out
form once. It also fills the trigger's name, which the previous markup
left empty.
Signing out revokes the hosted-login bearer sessions this installation
issued, then redirects to the provider's end_session_endpoint when the
discovery document advertises one. Applications that authenticate
against this installation therefore lose their session too; one pointed
straight at an identity provider is out of its reach and is unchanged.
Roles and permissions become rows rather than a closed enum, so a tenant
can extend the catalog without a migration, and a role can no longer be
granted a capability that is not in it. requireSameOrigin now compares
against the origin the request arrived on instead of the configured base
URL, which defaulted to http://localhost:8080 and so rejected every
write in a deployment that never set it.
Two bugs found while verifying in a browser:
- A profile picture never rendered. avatarURL built an absolute URL
from the configured base URL, and the page's own img-src 'self'
blocked it. The console's own pages now take a root-relative path;
only /connect/userinfo, read from another origin, needs an absolute
one, and it follows the request.
- absoluteURL preferred the configuration over the request while its
comment claimed the opposite, which also misdirected the logout
redirect.
Card layout: the footer was one two-column row shared between a label
and a forty-character identifier, so the identifier took the width and
crushed its label into three stacked words. Each pair is a line now, the
value truncates with the full text in the title, and cards align their
footers across a row.
2026-10-05 19:18:07 +02:00
< bananas-theme-selector id = "theme" > < / bananas-theme-selector >
< bananas-appearance-toggle id = "mode" > < / bananas-appearance-toggle >
< div data-account-menu > < / div >
2026-10-04 12:24:13 +02:00
< / nav >
< / header >
< main >
< section class = "hero profile-hero" >
< div >
< p class = "eyebrow" > YOUR ACCOUNT< / p >
< h1 > Profile< span > .< / span > < / h1 >
< p class = "lede" > Your name, picture, and the tenant roles you hold. Applications such as go-tell read the picture link and your role through the login endpoints.< / p >
< / div >
< / section >
< div id = "notice" class = "notice" hidden > < / div >
< section class = "profile-grid" >
< article class = "card profile-card" >
< h2 > Profile picture< / h2 >
< div class = "avatar-preview" >
< img id = "avatar-image" alt = "" width = "112" height = "112" hidden >
< span id = "avatar-fallback" class = "avatar-initials" > – < / span >
< / div >
< form id = "avatar-form" >
< label > Choose a picture < small > (JPEG, PNG, or GIF, up to 2 MiB)< / small > < input type = "file" id = "avatar-file" accept = "image/jpeg,image/png,image/gif" > < / label >
< div class = "profile-actions" >
< button type = "submit" id = "avatar-save" disabled > Upload picture< / button >
< button type = "button" class = "ghost" id = "avatar-remove" hidden > Remove picture< / button >
< / div >
< / form >
< label class = "muted" > Picture link < small > (embed it with a plain image tag)< / small >
< div class = "secret" > < code id = "avatar-url" > No picture uploaded< / code > < button id = "copy-avatar" type = "button" hidden > Copy< / button > < / div >
< / label >
< / article >
< article class = "card profile-card" >
< h2 > Account< / h2 >
< form id = "profile-form" >
< label > Display name< input name = "display_name" id = "display-name" maxlength = "200" required > < / label >
< label > Email< input id = "email" readonly > < / label >
< dl class = "facts" >
< div > < dt > System administrator< / dt > < dd id = "system-admin" > —< / dd > < / div >
< div > < dt > Member since< / dt > < dd id = "created-at" > —< / dd > < / div >
< div > < dt > Last login< / dt > < dd id = "last-login" > —< / dd > < / div >
< div > < dt > Name managed by< / dt > < dd id = "name-source" > —< / dd > < / div >
< / dl >
< button type = "submit" > Save profile< / button >
< / form >
< / article >
< article class = "card profile-card profile-wide" >
< h2 > Tenant roles< / h2 >
< div class = "table-wrap" > < table > < thead > < tr > < th > Tenant< / th > < th > Slug< / th > < th > Role< / th > < th > Capabilities< / th > < / tr > < / thead > < tbody id = "membership-list" > < / tbody > < / table > < / div >
< / article >
< / section >
< / main >
feat: account menu, database roles, and single logout
Bundles the console work from the current branch: the header account
control, database-backed roles and permissions, and signing out across
the applications that share this sign-in.
The account control replaces a separate identity chip and a separate
sign-out button, which meant the header carried two controls describing
one fact. web/account.js is mounted into every page by a placeholder and
owns the keyboard handling, the outside-click behaviour and the sign-out
form once. It also fills the trigger's name, which the previous markup
left empty.
Signing out revokes the hosted-login bearer sessions this installation
issued, then redirects to the provider's end_session_endpoint when the
discovery document advertises one. Applications that authenticate
against this installation therefore lose their session too; one pointed
straight at an identity provider is out of its reach and is unchanged.
Roles and permissions become rows rather than a closed enum, so a tenant
can extend the catalog without a migration, and a role can no longer be
granted a capability that is not in it. requireSameOrigin now compares
against the origin the request arrived on instead of the configured base
URL, which defaulted to http://localhost:8080 and so rejected every
write in a deployment that never set it.
Two bugs found while verifying in a browser:
- A profile picture never rendered. avatarURL built an absolute URL
from the configured base URL, and the page's own img-src 'self'
blocked it. The console's own pages now take a root-relative path;
only /connect/userinfo, read from another origin, needs an absolute
one, and it follows the request.
- absoluteURL preferred the configuration over the request while its
comment claimed the opposite, which also misdirected the logout
redirect.
Card layout: the footer was one two-column row shared between a label
and a forty-character identifier, so the identifier took the width and
crushed its label into three stacked words. Each pair is a line now, the
value truncates with the full text in the title, and cards align their
footers across a row.
2026-10-05 19:18:07 +02:00
< script src = "/assets/account.js" defer > < / script >
2026-10-04 12:24:13 +02:00
< script src = "/assets/profile.js" defer > < / script >
feat: account menu, database roles, and single logout
Bundles the console work from the current branch: the header account
control, database-backed roles and permissions, and signing out across
the applications that share this sign-in.
The account control replaces a separate identity chip and a separate
sign-out button, which meant the header carried two controls describing
one fact. web/account.js is mounted into every page by a placeholder and
owns the keyboard handling, the outside-click behaviour and the sign-out
form once. It also fills the trigger's name, which the previous markup
left empty.
Signing out revokes the hosted-login bearer sessions this installation
issued, then redirects to the provider's end_session_endpoint when the
discovery document advertises one. Applications that authenticate
against this installation therefore lose their session too; one pointed
straight at an identity provider is out of its reach and is unchanged.
Roles and permissions become rows rather than a closed enum, so a tenant
can extend the catalog without a migration, and a role can no longer be
granted a capability that is not in it. requireSameOrigin now compares
against the origin the request arrived on instead of the configured base
URL, which defaulted to http://localhost:8080 and so rejected every
write in a deployment that never set it.
Two bugs found while verifying in a browser:
- A profile picture never rendered. avatarURL built an absolute URL
from the configured base URL, and the page's own img-src 'self'
blocked it. The console's own pages now take a root-relative path;
only /connect/userinfo, read from another origin, needs an absolute
one, and it follows the request.
- absoluteURL preferred the configuration over the request while its
comment claimed the opposite, which also misdirected the logout
redirect.
Card layout: the footer was one two-column row shared between a label
and a forty-character identifier, so the identifier took the width and
crushed its label into three stacked words. Each pair is a line now, the
value truncates with the full text in the title, and cards align their
footers across a row.
2026-10-05 19:18:07 +02:00
< script type = "module" src = "__GO_LOOSE_THEME_ORIGIN__/rt/v1/components.js" data-bananas-components > < / script >
2026-10-04 12:24:13 +02:00
< / body >
< / html >