Chart 1.4.43 was built before PR #1032 bumped chart Chart.yaml in
the same commit, so its values.yaml had tag :91eeeed but the
hardcoded image refs in templates/api-deployment.yaml and
templates/ui-deployment.yaml stayed at :ff864e9 (the previous
bump from PR #1030). Sovereigns provisioned with chart 1.4.43
therefore still have the duplicate-sidebar bug — caught on
otech129 2026-05-05.
This bump pins the literal refs to :91eeeed, which is PR #1032's
commit SHA. Bootstrap-kit pin moves 1.4.43 → 1.4.44 so otech130+
get the PortalShell skip-inner-Sidebar logic.
Co-authored-by: Hati Yildiz <hatiyildiz@openova.io>
Symptom on otech127 2026-05-05: every page on the Sovereign Console
rendered TWO overlapping sidebars, where the inner one had broken
URLs like /provision//jobs (empty $deploymentId after the slash).
Clicking sidebar links failed because the broken sidebar was on top
and intercepted clicks.
Root cause: SovereignConsoleLayout (the chroot-route layout) mounts
SovereignSidebar with clean-root URLs (/jobs, /apps, etc.). The page
component (e.g. JobsPage) wraps its content in PortalShell, which
ALSO mounts the older Sidebar with deploymentId-templated URLs
(/provision/$deploymentId/jobs). On the chroot route there's no
deploymentId path param, so tan-stack renders /provision//jobs.
Fix: PortalShell skips its inner Sidebar when DETECTED_MODE.mode ===
'sovereign'. The outer SovereignSidebar (mounted by
SovereignConsoleLayout) is the correct chroot sidebar in that mode.
On mother-mode (/provision/$id/X) the inner Sidebar renders normally.
Bumps bp-catalyst-platform 1.4.42 → 1.4.43.
Co-authored-by: Hati Yildiz <hatiyildiz@openova.io>
The Sovereign Console at /sovereign/deployments rendered every row's FQDN
as a Link to=`/dashboard` regardless of which row was clicked. On contabo
(mother) this resolved to /sovereign/dashboard (the CURRENT user's
Sovereign), so clicking ANY row in the deployments list always
navigated to the same dashboard — breaking the operator's expectation
that "click row X to see deployment X's pages."
Fix: route each row to /provision/<row-id>/dashboard on the mother view
(Catalyst-Zero), and to /dashboard on the chroot Sovereign view (where
each Sovereign sees only its own deployment, so /dashboard is correct).
Mode resolved via the existing DETECTED_MODE singleton.
Bumps bp-catalyst-platform chart 1.4.40 → 1.4.41.
Co-authored-by: Hati Yildiz <hatiyildiz@openova.io>
Live on otech124 right now: /api/v1/sovereign/self returns 503
deployment-id-not-yet-stamped because:
- CATALYST_SELF_DEPLOYMENT_ID env is empty (orchestrator never patches
it, and #984's cutover-step-09-graduate idea wasn't merged either)
- The handler doesn't fall back to the local store
The deployment record IS imported on Sovereign (verified — POST
/api/v1/internal/deployments/import returns 200, persisted log
confirmed). Once the handler scans the store, /sovereign/self
returns the deploymentId and every chroot-aware UI Link
(/dashboard, /jobs, /apps, /cloud) finally renders correctly.
Without this, every <Link> built via useResolvedDeploymentId on
Sovereign mode produces /provision//<page> with empty id segment,
which the route validator rejects with 'Deployment id in the URL
is malformed' (founder report).
Closes the live regression on otech124.
Co-authored-by: hatiyildiz <hatice.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per founder report on otech122, the Sovereign Console /jobs page showed
all 'Pending' status — the imported deployment record's job snapshot
captured at mother's phase1-watching state, frozen forever.
The fix is small: useLiveJobsBackfill on Sovereign mode (DETECTED_MODE
=== 'sovereign') prefers /api/v1/sovereign/jobs which sovereign.go
already exposes — it reads HelmRelease history + recent K8s Jobs from
the local cluster's apiserver via in-cluster config and returns LIVE
status. The /api/v1/deployments/<id>/jobs path stays the default for
contabo monitor surface (mother view of an in-flight provision —
that's where the imported record IS the canonical view).
Also added credentials:'include' so the cookie reaches the endpoint.
Closes the user-reported 'all jobs Pending forever' on Sovereign
Console.
Co-authored-by: hatiyildiz <hatice.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Last leftover from PR #983's URL contract that PR #992 reverts undid.
PR #996 caught the auth_handover.go + router.tsx /console/dashboard
references but missed AuthCallbackPage.tsx:80. The Sovereign-side
PKCE callback after Keycloak login was navigating to a route that
doesn't exist in the consoleLayoutRoute tree.
Found while verifying otech124 mid-Phase-1.
Co-authored-by: hatiyildiz <hatice.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two problems surfaced live on otech122 (founder report):
1. SovereignSidebar.tsx still has /console/X paths.
PR #983 originally fixed this. PR #984 introduced the same fix in a
different shape. PR #992 (revert of broken redirect chain) reverted
#984 and accidentally reverted #983's SovereignSidebar fix too —
both PRs touched the same nav literals. PR #998 re-fixed
Sidebar.tsx (mother) but missed re-fixing SovereignSidebar.tsx.
Symptoms: clicking Settings on console.<sov-fqdn> goes to
/console/settings (route doesn't exist → 'Not found'); other nav
items fall through to wizard-side /provision//<page> handlers.
2. AppsPage.tsx app card row link is not chroot-aware.
On the mother monitor surface, the row link to <Link to='/app/$id'>
escapes /sovereign/provision/<dep-id>/ to /sovereign/app/<id>.
Fix: same DETECTED_MODE-aware pattern as PR #1000 used for JobsTable
and FlowPage.
3. SovereignConsoleLayout's settings dropdown navigate also still
pointed at /console/settings — fixed inline.
Co-authored-by: hatiyildiz <hatice.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same architectural reasoning as PR #1008 (subdomains/check). The wizard's
StepCredentials, StepDomain, StepCloud-creds and StepSSH all run BEFORE
the operator authenticates. Gating those endpoints on a session cookie
returned 401 to every anonymous visitor and blocked the only flow that
matters.
Move from rg (session-gated) to r (unauthenticated):
- /api/v1/credentials/validate (Hetzner token + project id)
- /api/v1/credentials/object-storage/validate (S3 creds)
- /api/v1/sshkey/generate (read-only ephemeral keypair)
- /api/v1/registrar/{r}/validate (Dynadot key+secret)
All four are read-only probes — they call the upstream API
(Hetzner/S3/Dynadot) with the operator-supplied credential and return
200/400 based on whether it works. No state change on success. The
upstream API itself is the auth gate (a wrong credential simply gets
rejected at the upstream).
/api/v1/registrar/{r}/set-ns stays in rg (session-gated) — it's
called from CreateDeployment which is itself post-auth.
Closes the wizard 401 the founder hit on Domain (BYO Dynadot) +
Credentials (Hetzner) steps trying otech with omantel.biz.
Co-authored-by: hatiyildiz <hatice.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* deploy: re-bump chart literal :b45a49f → :8ec8c01 (mistake-rollback fix)
PR #1006 rolled back to :b45a49f because the catalyst-api pod was
ImagePullBackOff for ~30s while pulling :8ec8c01. The image was IN
GHCR; the pull just took time. Pod recovered to Running on :8ec8c01,
THEN my rollback kicked in and reverted to :b45a49f — losing the
wizard credentials fix from PR #1004 that the founder needed.
Re-bump forward. :8ec8c01 contains useSubdomainAvailability's
credentials:'include' fix that closes the wizard 401 → false-502.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(catalyst-api): make /api/v1/subdomains/check public (no session required)
The wizard's Domain step renders BEFORE the operator authenticates —
PIN issue + verify happen AFTER they pick a subdomain. Requiring a
session cookie on /api/v1/subdomains/check forced 401 on every
anonymous visitor and trapped logged-out operators in a 'check
unavailable' state.
Move the route from rg (session-gated) to r (unauthenticated). Same
model as /auth/pin/issue: read-only public-facing endpoint with no
state change. Information disclosure is negligible — 'is this
subdomain taken?' is what DNS itself answers to anyone with a
resolver.
The handler routes to PDM (managed pool) or DNS (BYO); both are
read-only. PDM has its own rate-limiting middleware on the public
ingress, so anonymous spam is bounded by that.
Closes the wizard 401 the founder hit on otech119 Domain step.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: hatiyildiz <hatice.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #1006 rolled back to :b45a49f because the catalyst-api pod was
ImagePullBackOff for ~30s while pulling :8ec8c01. The image was IN
GHCR; the pull just took time. Pod recovered to Running on :8ec8c01,
THEN my rollback kicked in and reverted to :b45a49f — losing the
wizard credentials fix from PR #1004 that the founder needed.
Re-bump forward. :8ec8c01 contains useSubdomainAvailability's
credentials:'include' fix that closes the wizard 401 → false-502.
Co-authored-by: hatiyildiz <hatice.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Chart 1.4.35 referenced :8ec8c01 before the catalyst-build for that
SHA finished pushing to GHCR. Flux applied → catalyst-api pod stuck
ImagePullBackOff → wizard breaks ('worked few seconds then failed').
Roll the literal back to :b45a49f (the previous working SHA from
chart 1.4.34). Chart version stays 1.4.35 to avoid re-publishing
churn. The wizard credentials fix in :8ec8c01 will land when the
build catches up — at which point we manually re-bump the literal.
Co-authored-by: hatiyildiz <hatice.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(bp-catalyst-platform): bump 1.4.33 → 1.4.34 + literal :11dd19e → :b45a49f (#1000 cloud chroot + wizard banner)
* fix(wizard): include credentials on subdomain availability check fetch
The Domain step's POST /api/v1/subdomains/check was firing without
`credentials: 'include'`, so the catalyst_session cookie wasn't sent.
catalyst-api's RequireSession middleware returned 401, which the
wizard surfaced as 'Availability check failed (HTTP 401)' —
indistinguishable from a true upstream PDM failure.
Add credentials:'include'. Other session-gated wizard fetches already
have this; this one was missed.
Repro: open /sovereign/wizard signed-in, type a subdomain, see
'Availability check unavailable'. catalyst-api access log shows POST
.../subdomains/check → 401.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: hatiyildiz <hatice.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two operator-reported bugs:
1. Cloud sub-pages still escaped chroot. PR #998 closed Sidebar/JobsTable/
FlowPage but missed CloudPage (4 navigate sites), CloudListView (2),
UserAccessEditPage (2). Apply the same DETECTED_MODE-aware target
construction so /provision/<id>/cloud paths stay scoped under the
chroot on the mother monitoring view.
2. WizardPage auto-redirected signed-in operators with an inflight
deployment to /provision/<id>/dashboard, blocking the legitimate
case of starting a SECOND provision while the first is still in
flight (founder: 'maybe I'll provision one more').
Replace the auto-redirect with an inline banner at the top of the
wizard pointing at the inflight monitor. The wizard stays
interactive — operator can step through and Launch a second
deployment if they want, OR click 'Open monitor →' to resume the
first one.
Co-authored-by: hatiyildiz <hatice.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
While the operator monitors an in-flight Sovereign from the mothership
wizard surface (`console.openova.io/sovereign/provision/$deploymentId/...`),
every internal link MUST stay scoped under that prefix. Today, three
places escape the chroot to clean root paths intended for the
Sovereign's adult hostname:
1. Sidebar.tsx (mother-monitor sidebar): FLAT_NAV[*].to and SETTINGS_ITEM.to
were hardcoded to clean roots like '/jobs', '/cloud' — clicking a nav
item bounced the operator out of /provision/<id>/* to /sovereign/jobs
(which is either Sovereign-Console route on contabo's mothership view
= 404, or the Sovereign-on-clean-root on adult view = wrong context).
Restore the canonical /provision/$deploymentId/<page> TanStack template;
the params={{ deploymentId }} prop already feeds the substitution.
2. JobsTable.tsx (job row + parent-chip Links): `to=`/jobs/$jobId`` is
valid on the Sovereign adult surface but escapes the chroot on the
mother monitor view. Add a useJobLinkBuilder hook that returns
/provision/<id>/jobs/<jobId> on Catalyst-Zero hostnames and
/jobs/<jobId> on Sovereign hostnames.
3. FlowPage.tsx (canvas leaf-job click navigate): same chroot escape.
Same mode-aware target construction.
The chroot rule (founder framing): the operator CANNOT distinguish
'I'm monitoring my child being born under /provision/<id>/' from
'I'm at home on the adult Sovereign console' visually — every page,
sidebar, link, and chip must look identical (#983 pixel-byte-byte
contract). This commit closes the navigation half of that contract
on the mother side; PR #983 already covered the data-fetch half.
Closes the bug surfaced live on otech118 mid-provision: clicking Jobs
in the sidebar from /sovereign/provision/571a382deb47e50a/dashboard
sent the operator to /sovereign/jobs (404 / wrong scope), and a row
click sent them to /sovereign/jobs/571a382...:install-valkey instead
of /sovereign/provision/<id>/jobs/<id>:install-valkey.
Co-authored-by: hatiyildiz <hatice.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The reverts of #984/#987/#989 brought back three legacy /console/dashboard
redirects that PR #983 had originally cleaned up:
1. auth_handover.go:253 — default redirectTarget on the Sovereign-side
/auth/handover handler.
2. router.tsx:109 — index route's Sovereign-mode redirect.
3. router.tsx:163 — /auth/handover client-side safety-net redirect.
4. auth_handover_test.go fixture — keeps the test in sync.
Closes the loop on PR #983's URL contract.
Co-authored-by: hatiyildiz <hatice.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>