feat: add read-only mode for GitOps deployments#1349
feat: add read-only mode for GitOps deployments#1349opspawn wants to merge 3 commits intokagent-dev:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds an application-wide “read-only / GitOps mode” that blocks write operations in the backend authorizer and hides/guards write-capable UI paths and controls in the Next.js frontend.
Changes:
- Backend: introduce
ReadOnlyAuthorizerand wire it viaKAGENT_READ_ONLY=true, with unit tests. - Frontend: add
ReadOnlyProvider(NEXT_PUBLIC_READ_ONLY=true) and conditionally hide create/edit/delete controls across pages/components. - Frontend: add route guards for create/edit pages and skip onboarding in read-only mode.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| ui/src/components/sidebars/ChatItem.tsx | Hides chat session deletion UI when read-only. |
| ui/src/components/ReadOnlyProvider.tsx | Adds read-only React context + hook sourced from NEXT_PUBLIC_READ_ONLY. |
| ui/src/components/Header.tsx | Hides “Create” menus when read-only (desktop + mobile). |
| ui/src/components/AppInitializer.tsx | Skips onboarding wizard in read-only mode. |
| ui/src/components/AgentList.tsx | Replaces empty-state create CTA with GitOps messaging when read-only. |
| ui/src/components/AgentCard.tsx | Hides edit/delete overlay actions when read-only. |
| ui/src/app/servers/page.tsx | Hides add/remove MCP server UI and updates empty state for read-only. |
| ui/src/app/models/page.tsx | Hides model create/edit/delete UI and shows GitOps messaging when read-only. |
| ui/src/app/models/new/page.tsx | Adds read-only guard redirect for model create/edit page. |
| ui/src/app/layout.tsx | Wires ReadOnlyProvider at the app root. |
| ui/src/app/agents/new/page.tsx | Adds read-only guard redirect for agent create/edit page. |
| go/internal/httpserver/auth/authz_test.go | Adds tests for NoopAuthorizer and new ReadOnlyAuthorizer. |
| go/internal/httpserver/auth/authz.go | Implements ReadOnlyAuthorizer (allow GET only). |
| go/cmd/controller/main.go | Selects ReadOnlyAuthorizer when KAGENT_READ_ONLY=true. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const readOnly = useReadOnly(); | ||
| // Determine if in edit mode | ||
| const searchParams = useSearchParams(); | ||
| const isEditMode = searchParams.get("edit") === "true"; | ||
| const agentName = searchParams.get("name"); | ||
| const agentNamespace = searchParams.get("namespace"); | ||
|
|
||
| if (readOnly) { | ||
| redirect("/"); | ||
| } |
There was a problem hiding this comment.
redirect() from next/navigation is a server-only API; this page is marked "use client", so calling redirect("/") during render will fail at build/runtime. Use useRouter().replace("/") (typically inside a useEffect) and render null/a small placeholder while navigating, or refactor the guard into a server component wrapper where redirect() is supported.
| export default function ModelPage() { | ||
| const readOnly = useReadOnly(); | ||
|
|
||
| if (readOnly) { | ||
| redirect("/"); | ||
| } |
There was a problem hiding this comment.
redirect() is not supported in Client Components ("use client"). This guard will throw rather than navigating. Prefer a client-side redirect via useRouter().replace("/") in an effect (and return null while it runs), or move the read-only check to a server component where redirect() can be used.
ui/src/components/Header.tsx
Outdated
| </Link> | ||
| </DropdownMenuItem> | ||
| <DropdownMenuItem asChild onClick={handleMobileLinkClick}> | ||
| <Link href="/servers/new" className="gap-2 cursor-pointer w-full"> |
There was a problem hiding this comment.
The mobile "New MCP Server" link points to /servers/new, but there is no ui/src/app/servers/new route in the app (only /servers). This will 404 on mobile. Update the link to the existing /servers page (or add the missing route) to keep mobile/desktop behavior consistent.
| <Link href="/servers/new" className="gap-2 cursor-pointer w-full"> | |
| <Link href="/servers" className="gap-2 cursor-pointer w-full"> |
|
All 20 CI checks are passing. This PR directly addresses the feature request in #1344. Ready for review whenever you have a chance! |
|
CI is green (18/20, remaining 2 still running). This implements read-only mode for GitOps deployments (addresses #1344). All Copilot review feedback has been addressed. Ready for maintainer review when available. |
Add read-only mode to both the Go backend and Next.js frontend, allowing kagent to be deployed in GitOps environments where resources are managed declaratively. Backend: - Add ReadOnlyAuthorizer that allows VerbGet but rejects create/update/delete - Wire via KAGENT_READ_ONLY=true environment variable - Add comprehensive tests for the new authorizer Frontend: - Add ReadOnlyProvider context with useReadOnly() hook - Hide Create dropdown in Header navigation - Hide Edit/Delete overlays on agent cards and model rows - Hide chat session delete option - Hide Add/Remove buttons on MCP servers page - Add route guards redirecting /agents/new and /models/new to / - Skip onboarding wizard in read-only mode - Show 'Resources are managed via GitOps' in empty states - Wire via NEXT_PUBLIC_READ_ONLY=true environment variable Closes kagent-dev#1344 Signed-off-by: opspawn <opspawn@users.noreply.github.com>
- Replace server-only redirect() with client-side useRouter().replace() in agents/new and models/new pages (both marked "use client") - Fix mobile "New MCP Server" link to point to /servers instead of non-existent /servers/new route Signed-off-by: opspawn <opspawn@users.noreply.github.com>
Signed-off-by: opspawn <opspawn@users.noreply.github.com>
957c410 to
5b388f0
Compare
|
Closing in favor of #1346 by frank-bee which implements the same read-only mode for GitOps-managed deployments. Our implementation was parallel but theirs is further along. |
|
Closing in favor of #1346 by frank-bee which covers the same GitOps read-only mode functionality. |
Summary
Implements read-only mode for kagent (#1344), enabling GitOps-compatible deployments where resources are managed declaratively and the UI serves as a view-only dashboard.
Backend (Go)
go/internal/httpserver/auth/authz.go— implements theAuthorizerinterface, allowsVerbGetand rejectscreate/update/deletewith a clear error messageKAGENT_READ_ONLY=trueenvironment variable ingo/cmd/controller/main.goFrontend (Next.js)
useReadOnly()hook wired viaNEXT_PUBLIC_READ_ONLY=true/agents/newand/models/newredirect to/when read-onlyHow to test
Backend:
Frontend (read-only mode):
Then verify:
/agents/newand/models/newredirect to/Frontend (normal mode):
Verify all create/edit/delete functionality works as before.
Closes #1344