feat(app): node-override secrets UX + supabase scaffold refresh

- Secrets page: 'Node overrides' section (per-node values that shadow a shared
  secret on that node) — list/reveal/edit/delete + add; shared secrets show an
  'override: <node>' badge. Authority-only writes.
- API: GET /secrets/overrides, GET/PUT/DELETE /secrets/overrides/{node}/{name};
  OpenBaoBackend.list_node_overrides enumerates castle/nodes/*
- supabase scaffold: drop stale 'cat ~/.castle/secrets/...' + 'castle deploy',
  point at the dashboard Secrets page / castle apply
- tests: file-backend override endpoints (empty/400/404)

Verified live: primer's postgres override discoverable + full CRUD round-trip.
This commit is contained in:
2026-07-07 07:54:28 -07:00
parent c07a23e9a1
commit 01d0abd5ed
6 changed files with 327 additions and 11 deletions

View File

@@ -583,3 +583,29 @@ export function useDeleteSecret() {
onSuccess: () => qc.invalidateQueries({ queryKey: ["secrets"] }),
})
}
export function useSecretOverrides() {
return useQuery({
queryKey: ["secret-overrides"],
queryFn: () =>
apiClient.get<{ overrides: Record<string, string[]> }>("/secrets/overrides"),
})
}
export function useSetOverride() {
const qc = useQueryClient()
return useMutation({
mutationFn: ({ node, name, value }: { node: string; name: string; value: string }) =>
apiClient.put(`/secrets/overrides/${node}/${name}`, { value }),
onSuccess: () => qc.invalidateQueries({ queryKey: ["secret-overrides"] }),
})
}
export function useDeleteOverride() {
const qc = useQueryClient()
return useMutation({
mutationFn: ({ node, name }: { node: string; name: string }) =>
apiClient.delete(`/secrets/overrides/${node}/${name}`),
onSuccess: () => qc.invalidateQueries({ queryKey: ["secret-overrides"] }),
})
}