Documentation
¶
Overview ¶
Package kcp bootstraps kcp API resources.
Index ¶
- Constants
- Variables
- func AppendClusterPath(host, clusterPath string) stringdeprecated
- func ProviderEdgeServiceName(providerName string) string
- func ValidateProviders(enabled []string) error
- type AppAccessGrant
- type Bootstrapper
- func (b *Bootstrapper) Bootstrap(ctx context.Context) error
- func (b *Bootstrapper) ChildWorkspaceConfig(orgUUID, wsUUID string) *rest.Config
- func (b *Bootstrapper) ClearWorkspaceDeletionAnnotation(ctx context.Context, orgUUID, wsUUID string) error
- func (b *Bootstrapper) ControllersConfig() *rest.Config
- func (b *Bootstrapper) CreateMCPServer(ctx context.Context, clusterName, name, displayName, instructions string, ...) error
- func (b *Bootstrapper) DeleteChildWorkspace(ctx context.Context, orgUUID, wsUUID string) error
- func (b *Bootstrapper) DeleteMCPServer(ctx context.Context, clusterName, name string) error
- func (b *Bootstrapper) DeleteOrgMembership(ctx context.Context, orgUUID, userName string) error
- func (b *Bootstrapper) DeleteOrgMemberships(ctx context.Context, orgUUID string) error
- func (b *Bootstrapper) DeleteOrgProviderWorkspace(ctx context.Context, orgUUID, name string) error
- func (b *Bootstrapper) DeleteOrgWorkspace(ctx context.Context, orgUUID string) error
- func (b *Bootstrapper) DeleteProviderAPIBinding(ctx context.Context, orgUUID, wsUUID, bindingName string) error
- func (b *Bootstrapper) EnsureChildWorkspace(ctx context.Context, orgUUID, wsUUID string) error
- func (b *Bootstrapper) EnsureChildWorkspaceAdmin(ctx context.Context, orgUUID, wsUUID, rbacIdentity string) error
- func (b *Bootstrapper) EnsureChildWorkspaceDefaultMCPServer(ctx context.Context, orgUUID, wsUUID string) error
- func (b *Bootstrapper) EnsureChildWorkspaceDisplayName(ctx context.Context, orgUUID, wsUUID, displayName string) error
- func (b *Bootstrapper) EnsureChildWorkspaceRailgridBinding(ctx context.Context, orgUUID, wsUUID string) error
- func (b *Bootstrapper) EnsureDefaultMCPServer(ctx context.Context, clusterName string) error
- func (b *Bootstrapper) EnsureHubSystemNamespace(ctx context.Context) error
- func (b *Bootstrapper) EnsureOrgMembership(ctx context.Context, orgUUID, userName, role string) error
- func (b *Bootstrapper) EnsureOrgProviderWorkspace(ctx context.Context, orgUUID, name string) (string, error)
- func (b *Bootstrapper) EnsureOrgProvidersWorkspace(ctx context.Context, orgUUID string) error
- func (b *Bootstrapper) EnsureOrgWorkspace(ctx context.Context, orgUUID string) error
- func (b *Bootstrapper) EnsureProviderAPIBinding(ctx context.Context, ...) error
- func (b *Bootstrapper) EnsureProviderEdgeProxyGrant(ctx context.Context, orgUUID, wsUUID, providerName, subject string) error
- func (b *Bootstrapper) EnsureProviderEdgeService(ctx context.Context, orgUUID string, route EdgeRoute, ...) error
- func (b *Bootstrapper) EnsureWorkspaceAdmin(ctx context.Context, clusterName, rbacIdentity string) error
- func (b *Bootstrapper) GetChildWorkspaceClusterName(ctx context.Context, orgUUID, wsUUID string) (string, error)
- func (b *Bootstrapper) GetEdgeInstallTarget(ctx context.Context, orgUUID, wsUUID, name string) (*EdgeInstallTarget, error)
- func (b *Bootstrapper) GetMCPServerToken(ctx context.Context, clusterName, name string) (string, error)
- func (b *Bootstrapper) GetOrgClusterName(ctx context.Context, orgUUID string) (string, error)
- func (b *Bootstrapper) GetOrgMembershipRole(ctx context.Context, orgUUID, userName string) (string, error)
- func (b *Bootstrapper) GetOrgProviderWorkspace(ctx context.Context, orgUUID, name string) (*OrgProviderWorkspace, error)
- func (b *Bootstrapper) GetProviderEdgeRoute(ctx context.Context, orgUUID, providerName string) (*EdgeRoute, error)
- func (b *Bootstrapper) GetWorkspaceDeletionRequestedAt(ctx context.Context, orgUUID, wsUUID string) (*time.Time, bool, error)
- func (b *Bootstrapper) GetWorkspaceDisplayName(ctx context.Context, orgUUID, wsUUID string) (string, error)
- func (b *Bootstrapper) ListAppAccessGrants(ctx context.Context, orgUUID, wsUUID string) ([]AppAccessGrant, error)
- func (b *Bootstrapper) ListChildTeamWorkspaces(ctx context.Context, orgUUID string) ([]string, error)
- func (b *Bootstrapper) ListChildWorkspaces(ctx context.Context, orgUUID string) ([]string, error)
- func (b *Bootstrapper) ListEdgeInstallTargets(ctx context.Context, orgUUID string) ([]EdgeInstallTarget, error)
- func (b *Bootstrapper) ListMCPServers(ctx context.Context, clusterName string) ([]MCPServerInfo, error)
- func (b *Bootstrapper) ListOrgMembershipRoles(ctx context.Context, orgUUID string) (map[string]string, error)
- func (b *Bootstrapper) ListOrgMemberships(ctx context.Context, orgUUID string) ([]string, error)
- func (b *Bootstrapper) ListOrgProviderWorkspaces(ctx context.Context, orgUUID string) ([]OrgProviderWorkspace, error)
- func (b *Bootstrapper) ListOrgWorkspaces(ctx context.Context) ([]string, error)
- func (b *Bootstrapper) ListProviderAPIBindings(ctx context.Context, orgUUID, wsUUID string) (map[string]ProviderBinding, error)
- func (b *Bootstrapper) OrgProviderConfig(orgUUID, name string) *rest.Config
- func (b *Bootstrapper) OrgsConfig() *rest.Config
- func (b *Bootstrapper) PatchOrgMembershipRole(ctx context.Context, orgUUID, userName, role string) error
- func (b *Bootstrapper) RecordProviderEdgeBinding(ctx context.Context, orgUUID, providerName, wsUUID, edgeName string) error
- func (b *Bootstrapper) RemoveAppAccessGrant(ctx context.Context, orgUUID, wsUUID, bindingName string) error
- func (b *Bootstrapper) RemoveProviderEdgeProxyGrant(ctx context.Context, orgUUID, wsUUID, providerName string) error
- func (b *Bootstrapper) ResolveProviderEdgeRoute(ctx context.Context, orgUUID, providerName, backendURL string) (*EdgeRoute, error)
- func (b *Bootstrapper) RevokeChildWorkspaceAdmin(ctx context.Context, orgUUID, wsUUID, rbacIdentity string) error
- func (b *Bootstrapper) RevokeWorkspaceAdmin(ctx context.Context, clusterName, rbacIdentity string) error
- func (b *Bootstrapper) SetWorkspaceDeletionAnnotation(ctx context.Context, orgUUID, wsUUID string, at time.Time) error
- func (b *Bootstrapper) SetWorkspaceDisplayName(ctx context.Context, orgUUID, wsUUID, displayName string) error
- func (b *Bootstrapper) StaleClaimIdentities(ctx context.Context, orgUUID, wsUUID string) (map[string][]ClaimIdentityMismatch, error)
- func (b *Bootstrapper) UpdateMCPServer(ctx context.Context, clusterName, name, displayName, instructions string, ...) error
- func (b *Bootstrapper) UsersConfig() *rest.Config
- func (b *Bootstrapper) WithEnabledProviders(names []string) *Bootstrapper
- type ClaimIdentityMismatch
- type ClaimIdentityMismatchError
- type ClusterServiceTarget
- type EdgeInstallTarget
- type EdgeRoute
- type EmbeddedKCP
- type EmbeddedKCPOptions
- type MCPFederatedProviderInfo
- type MCPFederatedToolInfo
- type MCPServerInfo
- type OrgProviderWorkspace
- type ProviderBinding
- type ProviderClaim
Constants ¶
const ( // EdgeRouteWorkspaceAnnotation is the team workspace holding the edge. EdgeRouteWorkspaceAnnotation = "edges.railgrid.ai/route-workspace" // EdgeRouteEdgeAnnotation is the KubernetesCluster edge's name. EdgeRouteEdgeAnnotation = "edges.railgrid.ai/route-edge" )
Edge routing for org-owned providers.
A self-hosted provider's control half dials out — it reaches kcp itself, so Templates and Instances reconcile with no help. Its DATA half is the problem: /services/providers/{name}/** is a hub→provider call, and a provider in a tenant's own cluster has no address the hub can dial. The edge tunnel is the transport for that one hop (docs/byo-provider-edge-transport.md).
Routing must not be readable from anything the provider controls, or a tenant could point platform traffic wherever they liked (E-1). So the edge is recorded by the HUB, at registration, as annotations on the provider Workspace — an object in the org's tree that only the hub writes.
const HubSystemNamespace = "railgrid-hub"
HubSystemNamespace is the namespace inside root:railgrid:system:controllers that holds the hub's cross-replica coordination state: the controller leader-election Lease and the shared browser-session / app-access-code Secrets. It is hub-internal — no tenant, provider, or user identity is ever granted access to system:controllers.
const WorkspaceDeletionAnnotation = "tenants.railgrid.ai/deletion-requested-at"
WorkspaceDeletionAnnotation is the annotation key used to mark a kcp Workspace as soft-deleted. The soft-delete reconciler (roadmap step 8) reads this on every reconcile and triggers the cascade once the 30-day grace window from the annotation's RFC3339 value has elapsed. kept on the kcp Workspace (rather than a railgrid wrapper CRD) because the kcp Workspace IS the source of truth for workspace lifecycle.
const WorkspaceDisplayNameAnnotation = "tenants.railgrid.ai/display-name"
WorkspaceDisplayNameAnnotation is the annotation key used to mark the human-facing display name on a kcp Workspace. v1 stores it as an annotation rather than a separate CRD field because kcp's Workspace type doesn't carry a displayName slot. Editable via the REST PATCH endpoint.
Variables ¶
var ErrClaimIdentityMismatch = errors.New("permission claim identity does not match the bound provider")
ErrClaimIdentityMismatch is the sentinel callers match on to turn this into a client-visible refusal rather than a 500.
Functions ¶
func AppendClusterPath
deprecated
func ProviderEdgeServiceName ¶
ProviderEdgeServiceName is the hub-owned Service standing in front of one org-owned provider. Derived from the provider name so the hub can always name it without a lookup, and prefixed so it cannot collide with a Service a tenant created for an appliance.
func ValidateProviders ¶
ValidateProviders is a thin re-export of providers.ResolveEnabledBuiltins that discards the resolved spec list. Used at process start (server.Run) to fail fast on a bad --providers flag BEFORE embedded kcp boots — callers in this package import providers anyway for the registry, so the indirection only saves callers in pkg/hub from learning about the providers.BuiltinSpec type when they just want a yes/no answer.
Types ¶
type AppAccessGrant ¶
type AppAccessGrant struct {
// Binding is the ClusterRoleBinding name (the revocation handle).
Binding string `json:"binding"`
// App is the published instance name the grant opens.
App string `json:"app"`
// User is the platform User metadata.name the grant was issued to.
User string `json:"user"`
// CreatedAt is the binding's creation timestamp.
CreatedAt time.Time `json:"createdAt"`
}
AppAccessGrant is the portal-facing view of one published-app invitation: plain workspace RBAC, surfaced so tenant settings can show who can open which private app.
type Bootstrapper ¶
type Bootstrapper struct {
// contains filtered or unexported fields
}
Bootstrapper sets up the kcp workspace hierarchy and API exports.
func NewBootstrapper ¶
func NewBootstrapper(config *rest.Config) *Bootstrapper
NewBootstrapper creates a new bootstrapper.
func (*Bootstrapper) Bootstrap ¶
func (b *Bootstrapper) Bootstrap(ctx context.Context) error
Bootstrap creates the workspace hierarchy:
root:railgrid - Root railgrid workspace
root:railgrid:providers - Parent of per-provider sub-workspaces
root:railgrid:providers:{name} - One provider (restricted `provider` type)
root:railgrid:tenants:{uuid}:{ws}:{edge} - Tenant org/team/edge fleet
root:railgrid:system:controllers - ALL platform APIExports + schemas
root:railgrid:system:providers - Provider + CatalogEntry objects
root:railgrid:system:tenants - User/Organization/Membership objects
func (*Bootstrapper) ChildWorkspaceConfig ¶
func (b *Bootstrapper) ChildWorkspaceConfig(orgUUID, wsUUID string) *rest.Config
ChildWorkspaceConfig returns a rest.Config targeting the child Workspace at root:railgrid:tenants:{orgUUID}:{wsUUID}. Used by REST endpoints that operate inside a Workspace (e.g. the ServiceAccount surface) so they can mint a typed kube clientset without rebuilding path strings themselves.
func (*Bootstrapper) ClearWorkspaceDeletionAnnotation ¶
func (b *Bootstrapper) ClearWorkspaceDeletionAnnotation(ctx context.Context, orgUUID, wsUUID string) error
ClearWorkspaceDeletionAnnotation removes the soft-delete annotation from the Workspace, signalling undelete. Idempotent on already-absent. It performs one conditional update and returns conflicts to the REST layer, which re-reads the annotation before deciding whether a retry is safe. That boundary prevents a stale clear from deleting a newer deletion request.
func (*Bootstrapper) ControllersConfig ¶
func (b *Bootstrapper) ControllersConfig() *rest.Config
ControllersConfig returns a rest.Config targeting root:railgrid:system:controllers, where the platform APIExports and the hub's own coordination objects live.
func (*Bootstrapper) CreateMCPServer ¶
func (b *Bootstrapper) CreateMCPServer(ctx context.Context, clusterName, name, displayName, instructions string, readOnly bool) error
CreateMCPServer creates an MCPServer CR. The reconciler provisions identity.
func (*Bootstrapper) DeleteChildWorkspace ¶
func (b *Bootstrapper) DeleteChildWorkspace(ctx context.Context, orgUUID, wsUUID string) error
DeleteChildWorkspace removes the kcp Workspace at root:railgrid:tenants:{orgUUID}:{wsUUID}. Idempotent on NotFound.
func (*Bootstrapper) DeleteMCPServer ¶
func (b *Bootstrapper) DeleteMCPServer(ctx context.Context, clusterName, name string) error
DeleteMCPServer removes an MCPServer; its identity objects GC via owner refs.
func (*Bootstrapper) DeleteOrgMembership ¶
func (b *Bootstrapper) DeleteOrgMembership(ctx context.Context, orgUUID, userName string) error
DeleteOrgMembership removes a single Membership CR from the Org workspace. Idempotent on NotFound.
func (*Bootstrapper) DeleteOrgMemberships ¶
func (b *Bootstrapper) DeleteOrgMemberships(ctx context.Context, orgUUID string) error
DeleteOrgMemberships removes every Membership CR inside the Organization workspace at root:railgrid:tenants:{orgUUID}. Used by the soft-delete cascade right before tearing down the workspace itself, so the index sync sees a clean delta. Idempotent on NotFound / empty list.
func (*Bootstrapper) DeleteOrgProviderWorkspace ¶
func (b *Bootstrapper) DeleteOrgProviderWorkspace(ctx context.Context, orgUUID, name string) error
DeleteOrgProviderWorkspace deletes one org-owned provider workspace. kcp cascades everything inside it: the ServiceAccount and its token, the APIExport, the APIResourceSchemas, and the CatalogEntry — which is what removes the provider from the catalog registry. Idempotent (NotFound is a no-op).
APIBindings that tenant workspaces already hold against the deleted export are NOT removed here; per kcp's semantics they go NotReady. Callers that want a clean teardown should Disable the provider in each workspace first.
func (*Bootstrapper) DeleteOrgWorkspace ¶
func (b *Bootstrapper) DeleteOrgWorkspace(ctx context.Context, orgUUID string) error
DeleteOrgWorkspace removes the kcp Workspace at root:railgrid:tenants:{orgUUID}. Idempotent on NotFound. Cascade callers should ensure all child Workspaces and the in-workspace Memberships have already been removed; kcp will delete the LogicalCluster.
func (*Bootstrapper) DeleteProviderAPIBinding ¶
func (b *Bootstrapper) DeleteProviderAPIBinding(ctx context.Context, orgUUID, wsUUID, bindingName string) error
DeleteProviderAPIBinding removes the named provider APIBinding from the child workspace root:railgrid:tenants:{orgUUID}:{wsUUID}. NotFound is a no-op so the Disable action is idempotent. Counterpart to EnsureProviderAPIBinding.
func (*Bootstrapper) EnsureChildWorkspace ¶
func (b *Bootstrapper) EnsureChildWorkspace(ctx context.Context, orgUUID, wsUUID string) error
EnsureChildWorkspace materializes a kcp Workspace at root:railgrid:tenants:{orgUUID}:{wsUUID} of type `workspace` (see config/kcp/workspacetype-workspace.yaml). Used by the organization bootstrap controller to create the User's default team Workspace inside their personal Org so the portal can pin a default X-Railgrid-Workspace header. Idempotent: returns nil on AlreadyExists and blocks until the workspace reports Ready.
The hub-mediated rule from O-10 only applies to the Organization workspace itself; the child team Workspace IS tenant-accessible. This method is invoked from the org bootstrap controller with the hub's admin credentials so the WorkspaceType admission's bind check against tenants.railgrid.ai passes (same chain that already powers EnsureOrgWorkspace).
func (*Bootstrapper) EnsureChildWorkspaceAdmin ¶
func (b *Bootstrapper) EnsureChildWorkspaceAdmin(ctx context.Context, orgUUID, wsUUID, rbacIdentity string) error
EnsureChildWorkspaceAdmin grants cluster-admin in the child team Workspace to the given rbacIdentity. Thin wrapper over EnsureWorkspaceAdmin with the canonical child-workspace path. Idempotent.
func (*Bootstrapper) EnsureChildWorkspaceDefaultMCPServer ¶
func (b *Bootstrapper) EnsureChildWorkspaceDefaultMCPServer(ctx context.Context, orgUUID, wsUUID string) error
EnsureChildWorkspaceDefaultMCPServer seeds the "default" MCPServer CR inside the child team Workspace. Thin wrapper over EnsureDefaultMCPServer with the canonical child-workspace path. Idempotent.
func (*Bootstrapper) EnsureChildWorkspaceDisplayName ¶
func (b *Bootstrapper) EnsureChildWorkspaceDisplayName(ctx context.Context, orgUUID, wsUUID, displayName string) error
EnsureChildWorkspaceDisplayName stamps the display-name annotation only when the Workspace does not carry one yet. Reconcilers use this to give bootstrap-provisioned workspaces a human-readable default without clobbering a rename the user made since — the absence check runs inside the update loop, so a rename that lands mid-flight wins.
func (*Bootstrapper) EnsureChildWorkspaceRailgridBinding ¶
func (b *Bootstrapper) EnsureChildWorkspaceRailgridBinding(ctx context.Context, orgUUID, wsUUID string) error
EnsureChildWorkspaceRailgridBinding creates an APIBinding to root:railgrid:providers.core.railgrid.ai inside the child team Workspace, accepting the permission claims railgrid controllers need. This is what makes Edge, MCPServer, Placement, VirtualWorkload usable inside the user's default Workspace.
The legacy tenant-workspace path (CreateTenantWorkspace) used to create the same binding inside root:railgrid:tenants:{userID}. PR #211 retires that flow; the bootstrap controller now drives this method for every personal-Org default Workspace.
The tenancy.kcp.io `workspaces` claim IS accepted here. It does not widen the tenant user's own RBAC — a permission claim grants the APIExport's controllers (railgrid, running over the core.railgrid.ai virtual workspace) access to Workspace objects inside this child Workspace. The edge mount reconciler needs it: it creates an `edge`-typed mount Workspace per kubernetes Edge and watches it via Owns(&Workspace{}) (pkg/hub/controllers/edge/mount_reconciler.go). The core.railgrid.ai APIExport already declares this claim (config/kcp/apiexport-core.railgrid.ai.yaml); leaving it unaccepted is what produced the "exported but not specified" reconcile warnings on the binding.
Preventing tenants from creating arbitrary child workspaces is a SEPARATE control, enforced by the `workspace` WorkspaceType (limitAllowedChildren caps children to the leaf `edge` type — see config/kcp/workspacetype-workspace.yaml), not by withholding this claim.
func (*Bootstrapper) EnsureDefaultMCPServer ¶
func (b *Bootstrapper) EnsureDefaultMCPServer(ctx context.Context, clusterName string) error
EnsureDefaultMCPServer creates a "default" MCPServer CR in the tenant workspace if absent, so every tenant has one ready-to-use endpoint. The in-core reconciler provisions its identity. Idempotent.
func (*Bootstrapper) EnsureHubSystemNamespace ¶
func (b *Bootstrapper) EnsureHubSystemNamespace(ctx context.Context) error
EnsureHubSystemNamespace creates HubSystemNamespace in system:controllers. Idempotent on AlreadyExists, so every replica can call it concurrently at startup.
func (*Bootstrapper) EnsureOrgMembership ¶
func (b *Bootstrapper) EnsureOrgMembership(ctx context.Context, orgUUID, userName, role string) error
EnsureOrgMembership creates a Membership CR inside the Organization workspace at root:railgrid:tenants:{orgUUID} granting the given User the given role at scope=org. Idempotent — returns nil if a Membership with the same metadata.name already exists, regardless of role drift (an admin demoting a member is owned by a separate Role-patch endpoint per O-12 and never comes through this path).
metadata.name = userName so the existence check is a cheap Get on a known key, not a List+filter. PR #4 ships only the bootstrap path (personal-Org admin Membership for the User); manual Org membership management lands in PR #10.
func (*Bootstrapper) EnsureOrgProviderWorkspace ¶
func (b *Bootstrapper) EnsureOrgProviderWorkspace(ctx context.Context, orgUUID, name string) (string, error)
EnsureOrgProviderWorkspace materializes one org-owned provider workspace at root:railgrid:tenants:{orgUUID}:providers:{name}, creating the parent `providers` workspace first if needed. Blocks until Ready and returns the workspace's logical cluster ID (Workspace.spec.cluster) — the same value kcp embeds in the provider SA's token claims, which callers need to build qualified RBAC subjects. Idempotent.
func (*Bootstrapper) EnsureOrgProvidersWorkspace ¶
func (b *Bootstrapper) EnsureOrgProvidersWorkspace(ctx context.Context, orgUUID string) error
EnsureOrgProvidersWorkspace materializes the well-known `providers` child of an Org workspace (root:railgrid:tenants:{orgUUID}:providers) as a `universal` workspace, and blocks until it is Ready. Idempotent.
It is created lazily on the first org-provider registration rather than during org bootstrap: most Orgs never register one, and an empty universal workspace per Org would be pure overhead in the common case.
func (*Bootstrapper) EnsureOrgWorkspace ¶
func (b *Bootstrapper) EnsureOrgWorkspace(ctx context.Context, orgUUID string) error
EnsureOrgWorkspace creates a kcp Workspace at root:railgrid:tenants:{orgUUID} of type `organization` (see config/kcp/workspacetype-organization.yaml). Idempotent: returns nil on AlreadyExists. Blocks until the workspace is Ready so callers can immediately patch the corresponding Organization CR's status.
Per docs/organizations.md decision O-10, Org workspaces are hub-mediated only — tenants never receive a kubeconfig pointing here. This method is invoked from the Organization bootstrap controller with the hub's own admin config; no per-User RBAC is granted inside the workspace.
The "organization" WorkspaceType's defaultAPIBindings bring tenants.railgrid.ai (Organization, CatalogEntry, future Membership) and tenancy.kcp.io (Workspace for child team-workspace creation in PR #3) into the Org workspace.
func (*Bootstrapper) EnsureProviderAPIBinding ¶
func (b *Bootstrapper) EnsureProviderAPIBinding( ctx context.Context, orgUUID, wsUUID, bindingName, exportPath, exportName string, claims []ProviderClaim, ) error
EnsureProviderAPIBinding creates (or no-ops on AlreadyExists) an APIBinding named `bindingName` in the child workspace root:railgrid:tenants:{orgUUID}:{wsUUID}, pointing at exportPath/exportName.
Used by the server-side POST /api/orgs/{org}/workspaces/{ws}/providers/{name}/enable handler so the portal doesn't have to talk to /clusters/{cluster}/apis/... directly — the hub's user-facing kcp proxy pins every user to their User.Spec.DefaultCluster and would 403 any non-default workspace even when commit #220's per-workspace RBAC grants are in place. The proxy's defaultCluster check happens BEFORE forwarding to kcp, so even valid RBAC can't get through. Routing the enable action server- side via the kcp-admin client sidesteps that pre-check.
PermissionClaims state: Accepted iff the user ticked the claim in the confirmation dialog, Rejected otherwise. kcp refuses to mark the binding Bound when any provider-required claim is Rejected, so the response surfaces the mismatch to the user automatically.
func (*Bootstrapper) EnsureProviderEdgeProxyGrant ¶
func (b *Bootstrapper) EnsureProviderEdgeProxyGrant(ctx context.Context, orgUUID, wsUUID, providerName, subject string) error
EnsureProviderEdgeProxyGrant grants `subject` (the provider SA's cluster-qualified identity — see pkg/util/identity) the "proxy" verb on the edges provider's group (edges.railgrid.ai, resources kubernetesclusters + linuxservers + macosservers) in the child workspace root:railgrid:tenants:{orgUUID}:{wsUUID}. The edges provider's tunnel edgeproxy handler SAR-checks exactly this tuple (provider-sdk/tunnel/auth.go), so the grant is what lets a provider with CatalogEntry spec.edgeProxyAccess open background connections to the tenant's edges. Idempotent; subjects are reconciled on re-Enable so a provider workspace re-provision (new cluster ID → new qualified subject) heals on the next Enable.
func (*Bootstrapper) EnsureProviderEdgeService ¶
func (b *Bootstrapper) EnsureProviderEdgeService(ctx context.Context, orgUUID string, route EdgeRoute, target ClusterServiceTarget) error
EnsureProviderEdgeService reconciles the hub-owned edges.railgrid.ai/Service that fronts one org-owned provider.
spec.auth is "passthrough": a provider backend authorizes the END USER, so substituting the Service's own token would collapse per-user RBAC into "anyone who can reach the tunnel" (E-5). The object is hub-owned and fully overwritten on every reconcile, so a tenant editing its target has that edit reverted rather than silently redirecting platform traffic (E-4).
func (*Bootstrapper) EnsureWorkspaceAdmin ¶
func (b *Bootstrapper) EnsureWorkspaceAdmin(ctx context.Context, clusterName, rbacIdentity string) error
EnsureWorkspaceAdmin ensures cluster-admin is granted to rbacIdentity in the workspace identified by clusterName. Idempotent — safe to call on every login.
func (*Bootstrapper) GetChildWorkspaceClusterName ¶
func (b *Bootstrapper) GetChildWorkspaceClusterName(ctx context.Context, orgUUID, wsUUID string) (string, error)
GetChildWorkspaceClusterName returns the kcp logical-cluster short hash (e.g. "2mmugqjf6k4nwuve") for the child team Workspace at root:railgrid:tenants:{orgUUID}:{wsUUID}. kcp sets it in Workspace.spec.cluster when the workspace reaches phase Ready; EnsureChildWorkspace blocks on Ready, so by the time this method is called the field is populated. The short hash is the form kubectl / the kcp proxy address by — using the full path in kubeconfigs makes for ugly URLs and breaks tools that index on cluster name.
func (*Bootstrapper) GetEdgeInstallTarget ¶
func (b *Bootstrapper) GetEdgeInstallTarget(ctx context.Context, orgUUID, wsUUID, name string) (*EdgeInstallTarget, error)
GetEdgeInstallTarget resolves one edge by (workspace, name), returning (nil, nil) when it does not exist or the edges provider is not enabled in that workspace. Both cases mean the same thing to a caller checking a precondition — there is no such target — and distinguishing them in the error would leak whether a workspace has edges enabled to a caller who named a workspace they cannot otherwise see.
func (*Bootstrapper) GetMCPServerToken ¶
func (b *Bootstrapper) GetMCPServerToken(ctx context.Context, clusterName, name string) (string, error)
GetMCPServerToken reads the long-lived token for a named MCPServer by following status.tokenSecretRef. Returns "" (not an error) when the server or token is not provisioned yet, so the UI can show a "provisioning" state.
func (*Bootstrapper) GetOrgClusterName ¶
GetOrgClusterName returns the kcp logical cluster name of an Organization workspace at root:railgrid:tenants:{orgUUID} once it is Ready. The cluster name is what status.workspaceCluster on the Organization CR can record for observers that need the canonical kcp identifier rather than the human-readable path.
func (*Bootstrapper) GetOrgMembershipRole ¶
func (b *Bootstrapper) GetOrgMembershipRole(ctx context.Context, orgUUID, userName string) (string, error)
GetOrgMembershipRole returns the role of a single Membership in the Org workspace. NotFound if the user has no Membership; "" plus nil error if the Membership exists but has no role (shouldn't happen).
func (*Bootstrapper) GetOrgProviderWorkspace ¶
func (b *Bootstrapper) GetOrgProviderWorkspace(ctx context.Context, orgUUID, name string) (*OrgProviderWorkspace, error)
GetOrgProviderWorkspace returns one org-owned provider workspace, or (nil, nil) when it does not exist.
func (*Bootstrapper) GetProviderEdgeRoute ¶
func (b *Bootstrapper) GetProviderEdgeRoute(ctx context.Context, orgUUID, providerName string) (*EdgeRoute, error)
GetProviderEdgeRoute reads the binding back. Returns nil when the provider has none — a provider registered before edge transport existed, which stays on its declared backend URL rather than becoming unroutable.
func (*Bootstrapper) GetWorkspaceDeletionRequestedAt ¶
func (b *Bootstrapper) GetWorkspaceDeletionRequestedAt(ctx context.Context, orgUUID, wsUUID string) (*time.Time, bool, error)
GetWorkspaceDeletionRequestedAt reads the soft-delete annotation (WorkspaceDeletionAnnotation) from the child Workspace and returns the parsed timestamp. The bool reports whether the annotation was present at all (so callers can distinguish "no soft-delete requested" from "annotation present but malformed" — malformed surfaces as an error rather than a missing timestamp).
func (*Bootstrapper) GetWorkspaceDisplayName ¶
func (b *Bootstrapper) GetWorkspaceDisplayName(ctx context.Context, orgUUID, wsUUID string) (string, error)
GetWorkspaceDisplayName reads the display-name annotation. Empty string if absent. Workspace-not-found surfaces as IsNotFound.
func (*Bootstrapper) ListAppAccessGrants ¶
func (b *Bootstrapper) ListAppAccessGrants(ctx context.Context, orgUUID, wsUUID string) ([]AppAccessGrant, error)
ListAppAccessGrants lists the published-app access grants (labeled ClusterRoleBindings) in the child workspace. Same kcp-admin/proxy-avoidance rationale as ListProviderAPIBindings.
func (*Bootstrapper) ListChildTeamWorkspaces ¶
func (b *Bootstrapper) ListChildTeamWorkspaces(ctx context.Context, orgUUID string) ([]string, error)
ListChildTeamWorkspaces is ListChildWorkspaces without the infrastructure children an Org workspace can also hold — today just the well-known `providers` container for org-owned providers.
Every tenant-facing consumer wants this one: the workspace switcher, the admin workspace list, and the kcp proxy's authorizer all treat a child of an Org as "a team workspace a member can belong to". The providers container is neither — it holds no Memberships and no UMI rows, so surfacing it yields a workspace row that cannot be entered (selecting it sets an X-Railgrid-Workspace no membership matches, and every subsequent call 403s), and authorizing it would hand every org member access to the workspace that parents provider credentials.
ListChildWorkspaces stays unfiltered for lifecycle callers: the Org deletion cascade must delete the container too.
func (*Bootstrapper) ListChildWorkspaces ¶
ListChildWorkspaces returns the names of every child Workspace under root:railgrid:tenants:{orgUUID}. Empty list if the Org workspace is gone.
func (*Bootstrapper) ListEdgeInstallTargets ¶
func (b *Bootstrapper) ListEdgeInstallTargets(ctx context.Context, orgUUID string) ([]EdgeInstallTarget, error)
ListEdgeInstallTargets returns every KubernetesCluster edge across the Org's team workspaces, connected or not.
Workspaces where the edges provider is not enabled contribute nothing and are not an error: the resource simply is not served there, which kcp reports as NotFound or a no-match. That is the ordinary state of a workspace that never onboarded an edge, so treating it as a failure would make the whole listing fail for the one Org most likely to be asking.
Non-eligible edges are returned too, deliberately. "You have an edge but its agent is not connected" and "you have no edge at all" need different remedies, and the caller can only tell them apart if it sees both.
func (*Bootstrapper) ListMCPServers ¶
func (b *Bootstrapper) ListMCPServers(ctx context.Context, clusterName string) ([]MCPServerInfo, error)
ListMCPServers returns every MCPServer in the tenant workspace.
func (*Bootstrapper) ListOrgMembershipRoles ¶
func (b *Bootstrapper) ListOrgMembershipRoles(ctx context.Context, orgUUID string) (map[string]string, error)
ListOrgMembershipRoles returns user name → role for every Membership in the Organization workspace. Used to find the Org's admins when a child workspace is created, so O-15 (org admin = implicit admin in every child workspace) holds at the kcp RBAC layer too. Empty map if the Org workspace has no Memberships or has been deleted.
func (*Bootstrapper) ListOrgMemberships ¶
ListOrgMemberships returns the user names (Membership.metadata.name) of every Membership in the Organization workspace. Used by the soft-delete cascade to find which UMIs to mark / strip when an Org or one of its Workspaces enters / exits its grace window. Empty slice if the Org workspace has no Memberships or has been deleted.
func (*Bootstrapper) ListOrgProviderWorkspaces ¶
func (b *Bootstrapper) ListOrgProviderWorkspaces(ctx context.Context, orgUUID string) ([]OrgProviderWorkspace, error)
ListOrgProviderWorkspaces returns the provider sub-workspaces under root:railgrid:tenants:{orgUUID}:providers. An Org that has never registered one has no `providers` workspace at all, which is not an error — it lists empty.
func (*Bootstrapper) ListOrgWorkspaces ¶
func (b *Bootstrapper) ListOrgWorkspaces(ctx context.Context) ([]string, error)
ListOrgWorkspaces returns the names (UUIDs) of every Organization workspace at root:railgrid:tenants. Used by the soft-delete reconciler's Workspace branch to fan out across Orgs at resync time without standing up per-Org dynamic informers.
func (*Bootstrapper) ListProviderAPIBindings ¶
func (b *Bootstrapper) ListProviderAPIBindings(ctx context.Context, orgUUID, wsUUID string) (map[string]ProviderBinding, error)
ListProviderAPIBindings returns the set of Bound provider APIBindings present in the child workspace root:railgrid:tenants:{orgUUID}:{wsUUID}, keyed by provider name. Used by the GET /api/orgs/{org}/workspaces/{ws}/ providers/enabled handler so the portal can render the per-workspace "enabled providers" set on every workspace switch — without going through the kcp user-proxy, which 403s any non-default workspace path even when commit #220's per-workspace RBAC would have allowed the read.
Filtering rule: a binding counts as a "provider binding" iff its spec.reference.export.path names a provider workspace and its status.phase is Bound. Two shapes qualify:
root:railgrid:providers:<name> a platform provider root:railgrid:tenants:<orgUUID>:providers:<name> an org-owned provider
Org-owned exports count only for their OWN org, so a binding that somehow referenced another Org's provider is not reported as enabled here. The trailing segment is the provider name; the binding's own metadata.name is the value (existing convention is binding.name == provider.name).
func (*Bootstrapper) OrgProviderConfig ¶
func (b *Bootstrapper) OrgProviderConfig(orgUUID, name string) *rest.Config
OrgProviderConfig returns a rest.Config targeting one org-owned provider workspace. Used by the provisioner to mint that provider's ServiceAccount and kubeconfig without rebuilding path strings.
func (*Bootstrapper) OrgsConfig ¶
func (b *Bootstrapper) OrgsConfig() *rest.Config
OrgsConfig returns a rest.Config targeting the root:railgrid:tenants parent workspace. The Organization bootstrap controller uses this to create child Workspaces of type `organization` — one per Organization CR — at root:railgrid:tenants:{org-uuid}. The fleet location is unchanged by the system-workspace restructure.
func (*Bootstrapper) PatchOrgMembershipRole ¶
func (b *Bootstrapper) PatchOrgMembershipRole(ctx context.Context, orgUUID, userName, role string) error
PatchOrgMembershipRole updates the role on an existing Membership CR in the Org workspace. NotFound if the Membership doesn't exist. No-op when the role already matches.
func (*Bootstrapper) RecordProviderEdgeBinding ¶
func (b *Bootstrapper) RecordProviderEdgeBinding(ctx context.Context, orgUUID, providerName, wsUUID, edgeName string) error
RecordProviderEdgeBinding stamps the chosen edge onto the provider Workspace.
Called at registration, after the edge has been verified connected. Writing it here rather than on the CatalogEntry is the point: the CatalogEntry is authored by the tenant's own chart, and routing read from tenant-authored fields is routing the tenant controls.
func (*Bootstrapper) RemoveAppAccessGrant ¶
func (b *Bootstrapper) RemoveAppAccessGrant(ctx context.Context, orgUUID, wsUUID, bindingName string) error
RemoveAppAccessGrant revokes one published-app invitation by deleting its ClusterRoleBinding. It refuses to touch bindings that do not carry the app-access label so this endpoint can never delete unrelated RBAC. NotFound is a no-op for idempotent revocation.
func (*Bootstrapper) RemoveProviderEdgeProxyGrant ¶
func (b *Bootstrapper) RemoveProviderEdgeProxyGrant(ctx context.Context, orgUUID, wsUUID, providerName string) error
RemoveProviderEdgeProxyGrant deletes the ClusterRole/ClusterRoleBinding pair EnsureProviderEdgeProxyGrant created. NotFound is a no-op — Disable must succeed for providers that never had the grant.
func (*Bootstrapper) ResolveProviderEdgeRoute ¶
func (b *Bootstrapper) ResolveProviderEdgeRoute(ctx context.Context, orgUUID, providerName, backendURL string) (*EdgeRoute, error)
ResolveProviderEdgeRoute returns the usable edge route for an org-owned provider, reconciling the hub-owned Service on the way.
Implements providers.EdgeRouteResolver. Returns (nil, nil) when the provider has no recorded binding — one registered before edge transport existed, which keeps its old behaviour rather than becoming abruptly unroutable.
backendURL is the address the provider published about itself; it is parsed and validated as cluster DNS, never trusted as-is. A backend the hub cannot make sense of is an error, not a silent fallback: falling back would dial an address inside the tenant's cluster from the hub.
func (*Bootstrapper) RevokeChildWorkspaceAdmin ¶
func (b *Bootstrapper) RevokeChildWorkspaceAdmin(ctx context.Context, orgUUID, wsUUID, rbacIdentity string) error
RevokeChildWorkspaceAdmin is RevokeWorkspaceAdmin with the canonical child-workspace path. Idempotent.
func (*Bootstrapper) RevokeWorkspaceAdmin ¶
func (b *Bootstrapper) RevokeWorkspaceAdmin(ctx context.Context, clusterName, rbacIdentity string) error
RevokeWorkspaceAdmin removes the cluster-admin grant for rbacIdentity in the workspace identified by clusterName. Idempotent on NotFound. Only the caller's own per-user binding is touched; every other member keeps theirs.
func (*Bootstrapper) SetWorkspaceDeletionAnnotation ¶
func (b *Bootstrapper) SetWorkspaceDeletionAnnotation(ctx context.Context, orgUUID, wsUUID string, at time.Time) error
SetWorkspaceDeletionAnnotation stamps the kcp Workspace at root:railgrid:tenants:{orgUUID}:{wsUUID} with the soft-delete annotation (WorkspaceDeletionAnnotation) carrying the given timestamp. The soft-delete reconciler picks it up on its next poll. Once a deletion timestamp exists it is never replaced: the first request owns the recovery-window start, and retries are successful no-ops. A concurrent update is retried against a fresh Workspace so it cannot reset that clock.
func (*Bootstrapper) SetWorkspaceDisplayName ¶
func (b *Bootstrapper) SetWorkspaceDisplayName(ctx context.Context, orgUUID, wsUUID, displayName string) error
SetWorkspaceDisplayName stamps / overwrites the display-name annotation on the Workspace.
func (*Bootstrapper) StaleClaimIdentities ¶
func (b *Bootstrapper) StaleClaimIdentities(ctx context.Context, orgUUID, wsUUID string) (map[string][]ClaimIdentityMismatch, error)
StaleClaimIdentities reports, per provider bound in a workspace, any claim still pinned to a different copy of its dependency than the workspace uses.
This is the read-only twin of the Enable-time check, for surfacing the state rather than gating on it. It exists because the condition is silent everywhere else: swap a dependency and every provider claiming its resources keeps reporting Ready while quietly seeing none of them, so without something like this the first sign is a downstream 404 nobody connects to the swap.
Cost is one APIExport read per bound provider, on a call the portal already makes once per workspace switch.
func (*Bootstrapper) UpdateMCPServer ¶
func (b *Bootstrapper) UpdateMCPServer(ctx context.Context, clusterName, name, displayName, instructions string, readOnly bool) error
UpdateMCPServer patches an MCPServer's spec fields.
func (*Bootstrapper) UsersConfig ¶
func (b *Bootstrapper) UsersConfig() *rest.Config
UsersConfig returns a rest.Config targeting root:railgrid:system:tenants, where the User / Organization / Membership CR OBJECTS are stored (this replaces the former root:railgrid:users). The org *fleet* lives separately under root:railgrid:tenants (see OrgsConfig).
func (*Bootstrapper) WithEnabledProviders ¶
func (b *Bootstrapper) WithEnabledProviders(names []string) *Bootstrapper
WithEnabledProviders sets the subset of builtin providers the bootstrapper will write into root:railgrid:providers. Pass the value of the --providers flag; nil/empty selects every known builtin.
type ClaimIdentityMismatch ¶
type ClaimIdentityMismatch struct {
// Group and Resource identify the claim.
Group string
Resource string
// Declared is the identity the binding's export pins for this claim.
Declared string
// Actual is the identity of the export the workspace really binds for
// Group, and therefore the value the claim would have to carry to work.
Actual string
// ServingExportPath is the workspace path of that export — the copy the
// dependent would have to be repointed at.
ServingExportPath string
}
ClaimIdentityMismatch reports a claim whose pinned identity does not match the export actually serving that group in the target workspace.
type ClaimIdentityMismatchError ¶
type ClaimIdentityMismatchError struct {
Provider string
Mismatches []ClaimIdentityMismatch
// Repointable records whether the hub could have fixed this itself. It is
// false for a platform-wide export, whose single identity per claimed
// resource belongs to every org at once.
Repointable bool
}
ClaimIdentityMismatchError carries every mismatch found for one Enable.
func (*ClaimIdentityMismatchError) Error ¶
func (e *ClaimIdentityMismatchError) Error() string
func (*ClaimIdentityMismatchError) Is ¶
func (e *ClaimIdentityMismatchError) Is(target error) bool
type ClusterServiceTarget ¶
ClusterServiceTarget is a Kubernetes Service inside a tenant's cluster, parsed from an address the provider published about itself.
func ParseClusterServiceTarget ¶
func ParseClusterServiceTarget(backendURL string) (*ClusterServiceTarget, error)
ParseClusterServiceTarget reads a provider's declared backend URL as a cluster-DNS address and refuses anything else.
The provider's own CatalogEntry is the only thing that knows where it listens in the tenant's cluster — the chart's fullname and namespace are its choice, not something the hub can predict (an operator-mode install lands on `<release>-<chart>.<serve-ns>.svc`, not the `<name>.railgrid-provider-<name>.svc` a hub-side guess would produce). So the address is read from there but not trusted: it must be a `.svc` / `.svc.cluster.local` name, so the worst a tenant can aim this at is something inside the cluster the tunnel already terminates in. An IP, an external host, or a bare name is refused rather than silently proxied.
type EdgeInstallTarget ¶
type EdgeInstallTarget struct {
// Workspace is the team workspace UUID the edge lives in.
Workspace string
// WorkspaceDisplayName is that workspace's human label, when it has one.
WorkspaceDisplayName string
// Name is the KubernetesCluster's metadata.name.
Name string
// Connected reports an active agent tunnel (status.connected).
Connected bool
// Phase is status.phase, e.g. "AwaitingAgent" or "Connected". Carried so
// the UI can explain a not-Connected edge rather than just greying it out.
Phase string
// AgentVersion is the railgrid build running on the agent, when reported.
AgentVersion string
}
EdgeInstallTarget is one candidate cluster for a self-hosted provider, flattened across the Org's team workspaces.
Connected is the field that decides eligibility. An edge whose agent has never dialed in, or whose tunnel has dropped, is a cluster the hub cannot reach — installing a provider there produces a backend nothing can call.
type EdgeRoute ¶
type EdgeRoute struct {
// WorkspaceUUID is the team workspace holding the edge and the Service.
WorkspaceUUID string
// EdgeName is the KubernetesCluster edge whose agent carries the tunnel.
EdgeName string
// ServiceName is the hub-owned edges.railgrid.ai/Service. Derived, never read
// from tenant input.
ServiceName string
// Cluster is WorkspaceUUID's kcp logical-cluster ID — what the edges proxy
// path addresses. Empty until ResolveProviderEdgeRoute has filled it in.
Cluster string
}
EdgeRoute is where a self-hosted provider's backend is reached: which workspace holds the edge, which edge, and the hub-owned Service standing in front of the provider inside the tenant's cluster.
type EmbeddedKCP ¶
type EmbeddedKCP struct {
// contains filtered or unexported fields
}
EmbeddedKCP wraps a kcp server that runs in-process.
func NewEmbeddedKCP ¶
func NewEmbeddedKCP(opts EmbeddedKCPOptions) *EmbeddedKCP
NewEmbeddedKCP creates a new embedded kcp instance.
func (*EmbeddedKCP) AdminConfig ¶
func (e *EmbeddedKCP) AdminConfig() *rest.Config
AdminConfig returns a rest.Config for the kcp admin user. This should only be called after Ready() returns.
func (*EmbeddedKCP) AdminKubeconfigPath ¶
func (e *EmbeddedKCP) AdminKubeconfigPath() string
AdminKubeconfigPath returns the path to the admin kubeconfig file.
func (*EmbeddedKCP) Ready ¶
func (e *EmbeddedKCP) Ready() <-chan struct{}
Ready returns a channel that is closed when kcp is ready to serve requests.
type EmbeddedKCPOptions ¶
type EmbeddedKCPOptions struct {
RootDir string
SecurePort int
BindAddress string
BatteriesInclude []string
// TLS certificate/key files for the KCP API server.
// When set, KCP uses these instead of auto-generating self-signed certs.
// This is important in Kubernetes where pod IPs change on restart.
TLSCertFile string
TLSKeyFile string
// ShardExternalURL maps to kcp's --shard-external-url flag. It's the URL
// kcp publishes into APIExportEndpointSlice.status.endpoints[].url and
// CachedResourceEndpointSlice.status.endpoints[].url for outside consumers
// to dial. Empty defaults to the auto-detected external address (for an
// embedded kcp bound to 127.0.0.1 that's "https://127.0.0.1:6443") which
// is unreachable from inside a kind pod — set this to
// "https://host.docker.internal:6443" or similar when other workloads
// (e.g. the kro multicluster controller) need to dial back.
ShardExternalURL string
// ShardVirtualWorkspaceURL maps to kcp's --shard-virtual-workspace-url flag.
// This is the URL embedded into APIExportEndpointSlice endpoints —
// `--shard-external-url` only updates Shard.spec.externalURL, but the
// endpoint URLs are derived from Shard.spec.virtualWorkspaceURL (kcp
// keeps these split so the VW server can run on a separate host).
// Same default-and-override semantics as ShardExternalURL — in
// practice both want the same value for a single-shard embedded
// dev setup.
ShardVirtualWorkspaceURL string
// StaticAuthTokens are bearer tokens that kcp should accept directly
// via its token-auth-file mechanism. This allows static token users
// to be authenticated natively by kcp (needed for workspace mounts).
StaticAuthTokens []string
// OIDC options for native kcp authentication. When OIDCIssuerURL and
// OIDCClientID are both set, kcp will verify bearer tokens against the
// configured issuer and run requests as the resulting OIDC identity.
//
// Defaults are tuned to match the proxy/User CRD identity scheme
// (User.Spec.RBACIdentity = "railgrid:<email>", see pkg/server/auth/handler.go):
// UsernameClaim = "email"
// UsernamePrefix = "railgrid:"
// GroupsClaim = "groups"
// GroupsPrefix = "railgrid:"
OIDCIssuerURL string
OIDCClientID string
OIDCCAFile string
OIDCUsernameClaim string
OIDCUsernamePrefix string
OIDCGroupsClaim string
OIDCGroupsPrefix string
}
EmbeddedKCPOptions contains configuration for the embedded kcp server.
type MCPFederatedProviderInfo ¶
type MCPFederatedProviderInfo struct {
Name string `json:"name"`
DisplayName string `json:"displayName,omitempty"`
Reachable bool `json:"reachable"`
Message string `json:"message,omitempty"`
Tools []MCPFederatedToolInfo `json:"tools,omitempty"`
}
MCPFederatedProviderInfo mirrors railgridv1alpha1.FederatedMCPProvider for the portal.
type MCPFederatedToolInfo ¶
type MCPFederatedToolInfo struct {
Name string `json:"name"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
}
MCPFederatedToolInfo mirrors railgridv1alpha1.FederatedMCPTool for the portal.
type MCPServerInfo ¶
type MCPServerInfo struct {
Name string `json:"name"`
DisplayName string `json:"displayName,omitempty"`
Instructions string `json:"instructions,omitempty"`
ReadOnly bool `json:"readOnly,omitempty"`
Phase string `json:"phase,omitempty"`
URL string `json:"url,omitempty"`
// FederatedProviders is the live tool inventory the reconciler stamped on
// status.federatedProviders — which providers this server federates and the
// tools each advertises to it.
FederatedProviders []MCPFederatedProviderInfo `json:"federatedProviders,omitempty"`
// ToolsRefreshedTime is when the inventory above was last recomputed (RFC3339).
ToolsRefreshedTime string `json:"toolsRefreshedTime,omitempty"`
}
MCPServerInfo is a portal-facing view of an MCPServer CR.
type OrgProviderWorkspace ¶
OrgProviderWorkspace is one org-owned provider sub-workspace as listed from kcp. It reports what exists in the workspace tree, independent of whether the provider's chart has been installed and has registered a CatalogEntry yet — that gap is exactly the "registered but not yet connected" state the org admin UI needs to show.
type ProviderBinding ¶
type ProviderBinding struct {
// Name is the APIBinding's metadata.name (by convention, the provider name).
Name string
// ExportPath is the workspace path of the APIExport it binds.
ExportPath string
// SelfHosted is true when ExportPath is under the tenant fleet, i.e. the
// binding targets an Org's own provider rather than a platform one.
SelfHosted bool
// Terminating is true when the binding has been deleted but kcp's cascade
// cleanup (which removes every CR of the bound APIs first) has not finished.
// The binding still serves until then, so it still counts as enabled.
Terminating bool
// DeletionBlocked is kcp's explanation of what is holding a terminating
// binding's deletion open — typically leftover CR finalizers whose
// controller is gone. Empty when deletion is progressing normally or the
// binding is not terminating.
DeletionBlocked string
}
ProviderBinding describes one bound provider APIBinding in a workspace.
SelfHosted is the field that earns this type: once an Org can self-host a provider under the same name as the platform one, "is `edges` enabled here?" stops being a yes/no question. A workspace that enabled the platform edges before the Org started self-hosting still points at the platform export, and showing that as plain "Enabled" would tell the user they are running their own instance when they are not. Switching is a Disable + Enable, not an in-place retarget, so the distinction has to survive all the way to the UI.
type ProviderClaim ¶
ProviderClaim is the wire shape the REST handler hands EnsureProviderAPIBinding — one entry per permission claim the provider DECLARED in its CatalogEntry, plus a flag whether the user accepted or rejected it in the Enable confirmation dialog. Mirrors providers.PermissionClaim but lives here so the bootstrap package stays free of an import on pkg/hub/providers.