{
  "markdown": "# @nogoo9/no-crd\n\n<p align=\"center\">\n  <img src=\"docs/public/logo.png\" alt=\"nogoo9 logo\" width=\"200\" height=\"200\" />\n</p>\n\n<p align=\"center\">\n  <img src=\"docs/public/dashboard_screenshot.png\" alt=\"no-crd Dashboard\" width=\"800\" />\n</p>\n\n> **Agent-Driven, On-Demand Pod Orchestration in Kubernetes — Without Custom Resource Definitions.**\n\n[![npm version](https://img.shields.io/npm/v/@nogoo9%2Fno-crd.svg?color=c65f5f&logo=npm&logoColor=ffffff&style=flat-square)](https://www.npmjs.com/package/@nogoo9/no-crd) [![npm downloads](https://img.shields.io/npm/dm/@nogoo9%2Fno-crd.svg?color=c65f5f&logo=npm&logoColor=ffffff&style=flat-square)](https://www.npmjs.com/package/@nogoo9/no-crd) [![Documentation](https://img.shields.io/badge/docs-GitHub_Pages-5f7f9f.svg?logo=github&logoColor=ffffff&style=flat-square)](https://nogoo9.github.io/no-crd/) [![License](https://img.shields.io/npm/l/@nogoo9%2Fno-crd.svg?color=c65f5f&style=flat-square)](https://github.com/nogoo9/no-crd/blob/main/LICENSE) [![Coverage Status](https://img.shields.io/coveralls/github/nogoo9/no-crd?color=6fa37f&style=flat-square)](https://coveralls.io/github/nogoo9/no-crd?branch=main) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/nogoo9/no-crd/badge)](https://securityscorecards.dev/viewer/?uri=github.com/nogoo9/no-crd) [![Semgrep](https://img.shields.io/badge/Security-Semgrep-80679c?logo=semgrep&logoColor=ffffff&style=flat-square)](https://semgrep.dev/)\n\n[![Model Context Protocol](https://img.shields.io/badge/MCP-Server-d87258?style=flat-square)](https://modelcontextprotocol.io) [![Built with Antigravity](https://img.shields.io/badge/Built%20with-Antigravity-4f7ac7?logo=google&logoColor=ffffff&style=flat-square)](https://antigravity.google) [![Powered by Gemini](https://img.shields.io/badge/Powered%20by-Gemini-7b66c4?logo=google&logoColor=ffffff&style=flat-square)](https://deepmind.google/technologies/gemini/)\n\n[![Bun](https://img.shields.io/badge/Bun-%3E%3D1.3.11-b85b75?logo=bun&logoColor=ffffff&style=flat-square)](https://bun.sh) [![Deno](https://img.shields.io/badge/Deno-compatible-4a4e57?logo=deno&logoColor=ffffff&style=flat-square)](https://deno.land) [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22.14.0-4e824e?logo=node.js&logoColor=ffffff&style=flat-square)](https://nodejs.org)\n\n`@nogoo9/no-crd` is a lightweight, cross-runtime Model Context Protocol (MCP) server that empowers AI agents and APIs to dynamically spawn, route to, and manage ephemeral containerized sandboxes on standard Kubernetes (k8s/k3s) clusters — **without requiring Custom Resource Definitions (CRDs)**, cluster-level operators, or elevated RBAC permissions. \n\nIt provides JupyterHub-like dynamic pod lifecycle management but is completely agnostic to actual workloads and supports multi-runtime execution under **Bun**, **Deno**, and **Node.js**.\n\n📚 **For detailed guides, API reference, and configuration options, visit the public [Documentation Website](https://nogoo9.github.io/no-crd/) or access the built-in documentation served directly at `/docs/` (e.g. `http://localhost:3000/docs/`) when running the server.**\n\n---\n\n## 🗺️ Choose Your Path\n\nTo get started with `@nogoo9/no-crd`, select the track that matches your goals:\n\n*   **Track A: AI Agent & MCP Users** $\\rightarrow$ [MCP Client Setup Guide](https://nogoo9.github.io/no-crd/mcp/guide.html)\n    *   *Best for:* Connecting `no-crd` to **Cursor**, **Claude Desktop**, **Cline**, or **Roo Code** to let your agent manage pods.\n*   **Track B: Platform & Service Deployers** $\\rightarrow$ [SSO & Routing Deployment Guide](https://nogoo9.github.io/no-crd/deploy/architecture.html)\n    *   *Best for:* Deploying a multi-tenant sandbox service in your Kubernetes cluster with **Keycloak OIDC**, **stateless cookies**, and **reverse proxy routing**.\n*   **Track C: SDK & Code Contributors** $\\rightarrow$ [SDK & Contributor Guide](https://nogoo9.github.io/no-crd/developer/contributing.html)\n    *   *Best for:* Programmatically controlling pods via the **TypeScript SDK** or contributing to the core runtime codebase.\n\n---\n\n## 🚀 Key Features\n\n- **No CRDs Required:** Runs directly against core Kubernetes resources (Pods, ConfigMaps, ServiceAccounts). Highly portable, secure, and compatible with restricted/managed environments (EKS, GKE, K3s).\n- **Agent Sandbox Spawner:** Specialized spawner tools that automate workspace provisioning with context validation, init containers, IAM roles, pre-stop hooks, and lifecycle sync.\n- **ConfigMap-Based Templates:** Store, version, and load reusable pod templates stored as standard Kubernetes ConfigMaps.\n- **Local Filesystem Templates:** Bake YAML/JSON pod templates into Docker images or mount them from host paths — with built-in defaults shipped in the package.\n- **Isomorphic Multi-Runtime SDK:** Imports seamlessly as a composable programmatic SDK or MCP server running under Node.js, Bun, or Deno.\n- **Workspace Routing Proxy (Experimental):** Built-in reverse proxy routing that dynamically pipes traffic to running container IPs with secure user token ownership verification, path-scoped session cookies (`nocr_token` and `nocr_sess`), and automatic HMAC-signed session management for short-lived token resilience.\n- **Experimental JWT Authentication:** Built-in OAuth token validation engine supporting HS256, RS256, ES256, JWKS endpoints, and OAuth 2.0 Token Introspection (RFC 7662).\n- **Embedded Web UI App:** Exposes an interactive web-based Pod Manager interface featuring a light/dark theme toggle, client-side PKCE OIDC login with proactive silent token refresh, and workspace file preview rendering (supporting HTML sandboxed iframes and custom Markdown rendering).\n\n---\n\n## ⚡ Quick Start\n\n### Run Standalone via NPX (No Installation)\n```bash\n# Option 1: Start HTTP/SSE server on port 3000\nnpx @nogoo9/no-crd --transport http --port 3000\n\n# Option 2: Run over standard input/output (stdio) for local IDE agents\nnpx @nogoo9/no-crd --transport stdio --mode cluster\n```\n\n### Install Globally\n```bash\nnpm install -g @nogoo9/no-crd\nnocrd9 --transport stdio --mode cluster\n```\n\n### Run via Docker\n```bash\ndocker run -d -p 3000:3000 \\\n  -v \"$HOME/.kube/config:/app/.kube/config:ro\" \\\n  -e KUBECONFIG=/app/.kube/config \\\n  ghcr.io/nogoo9/no-crd:latest\n```\n\n---\n\n## ⚙️ Configuration & Environment Variables\n\nThe server and command-line utility are configurable using CLI options or environment variables. Below is the quick reference table of all settings:\n<!-- CONFIG_TABLES_START -->\n\n### 🔌 Server Configuration\n\n| CLI Option | Environment Variable | Default | Allowed Values | Description |\n|---|---|---|---|---|\n| `-t, --transport` | `TRANSPORT` | `http` | `http`, `stdio`, `both` | Server transport mode. `both` fires up both transports simultaneously. |\n| `-p, --port` | `PORT` | `3000` | Number | HTTP server port for SSE transport. |\n| `-H, --host` | `HOST` | `0.0.0.0` | String | Host interface to bind the HTTP/SSE server to. |\n| `--base-url` | `BASE_URL` | `\"\"` | Path string | Base URL path prefix for hosting behind a reverse proxy (e.g. `/gateway/no-crd`). |\n| - | `STATELESS` | `false` | `true`, `false` | Enable stateless request handling (no session affinity). |\n| `-l, --log-level` | `LOG_LEVEL` | `info` | `debug`, `info`, `warning`, `error`, `fatal` | Logging verbosity filter. |\n| - | `LOG_FILE` | `nogoo9-mcp.log` | String | Output file path for file logging. |\n| - | `RATE_LIMIT_MAX` | `100` | Number | Maximum requests allowed per window for rate limited routes. |\n| - | `RATE_LIMIT_WINDOW` | `60000` | Number | Time window in milliseconds for rate limited routes. |\n| `--proxy-timeout` | `PROXY_TIMEOUT` | `120000` | Number | Timeout in milliseconds for the routing proxy upstream requests. |\n| `--proxy-keep-alive` | `PROXY_KEEP_ALIVE` | `true` | `true`, `false` | Enable TCP keep-alive for the routing proxy upstream requests. |\n\n### 🔒 TLS Configuration\n\n| CLI Option | Environment Variable | Default | Allowed Values | Description |\n|---|---|---|---|---|\n| `--tls-cert` | `TLS_CERT` | - | Path string | Path to TLS certificate file to enable HTTPS. |\n| `--tls-key` | `TLS_KEY` | - | Path string | Path to TLS private key file to enable HTTPS. |\n| `--tls-ca` | `TLS_CA` | - | Path string | Path to TLS CA certificate file for HTTPS client/verification. |\n| - | `NODE_TLS_REJECT_UNAUTHORIZED` | `true` | `0 (false)`, `1 (true)` | Set to `0` to bypass TLS verification (for development/testing only). |\n\n### 🌐 CORS Configuration\n\n| CLI Option | Environment Variable | Default | Allowed Values | Description |\n|---|---|---|---|---|\n| `--cors-origin` | `CORS_ALLOWED_ORIGIN`, `CORS_ORIGIN` | `*` | String | CORS Allowed Origin header. |\n| `--cors-methods` | `CORS_ALLOWED_METHODS`, `CORS_METHODS` | `GET, POST, OPTIONS` | String | CORS Allowed Methods header. |\n| `--cors-headers` | `CORS_ALLOWED_HEADERS`, `CORS_HEADERS` | `Content-Type, Authorization, mcp-protocol-version, mcp-session-id` | String | CORS Allowed Headers header. |\n| `--cors-allow-credentials` | `CORS_ALLOW_CREDENTIALS`, `CORS_CREDENTIALS` | `false` | `true`, `false` | Enable CORS Access-Control-Allow-Credentials header. |\n| `--cors-expose-headers` | `CORS_EXPOSED_HEADERS`, `CORS_EXPOSED` | `mcp-session-id, x-refreshed-token` | String | Custom CORS Access-Control-Expose-Headers header. |\n| `--cors-max-age` | `CORS_MAX_AGE` | - | Number | Custom CORS Access-Control-Max-Age header in seconds. |\n\n### ☸️ Kubernetes Configuration\n\n| CLI Option | Environment Variable | Default | Allowed Values | Description |\n|---|---|---|---|---|\n| `-m, --mode` | `MODE` | `cluster` | `cluster`, `namespaced` | Kubernetes access scope. `namespaced` locks operations to a single namespace. |\n| `-n, --namespace` | `NAMESPACE`, `DEFAULT_NAMESPACE` | `nogoo9` | String | Default Kubernetes namespace for operations. |\n| `--disable-permission-checks` | `DISABLE_PERMISSION_CHECKS` | `false` | `true`, `false` | Disable Kubernetes RBAC permission checks and assume all tools are enabled. |\n| `--managed-only` | `MANAGED_ONLY` | `true` | `true`, `false` | When true, pod tools only operate on pods managed by this server (`nogoo9/managed-by` label). No one bypasses this, not even admins. See [ADR-008](docs/decisions/ADR-008-managed-only-pod-access-control.md). |\n| `--default-workspace-port` | `DEFAULT_WORKSPACE_PORT` | - | Number | Default target port inside the workspace pods to proxy traffic to. |\n| - | `REGISTRY_URL` | - | URL string | Target container registry URL to query for images (e.g. `http://localhost:5001`). |\n| - | `TEMPLATES_DIR` | - | Path string | Path to local directory containing pod template files (YAML/JSON). See [ADR-001](docs/decisions/ADR-001-template-file-format.md). |\n| - | `BUILTIN_TEMPLATES` | `true` | `true`, `false` | Set to `false` to disable built-in templates shipped with the package. |\n\n### 🔑 Authentication Configuration\n\n| CLI Option | Environment Variable | Default | Allowed Values | Description |\n|---|---|---|---|---|\n| `--auth-enabled` | `AUTH_ENABLED` | `false` | `true`, `false` | Enables JWT token authentication on MCP tools and route proxy. |\n| - | `JWT_VERIFICATION_REQUIRED` | `true` | `true`, `false` | Enable/disable JWT signature verification (signature checks). |\n| - | `JWT_SECRET` | - | String | Symmetric HMAC-SHA256 secret for token verification. |\n| - | `JWT_PUBLIC_KEY` | - | String | PEM encoded RSA/ECDSA public key for asymmetric token verification. |\n| - | `JWKS_URI` | - | URL string | Remote JWKS endpoint URL to dynamically retrieve verification keys. |\n| - | `INTROSPECTION_ENDPOINT`, `JWT_INTROSPECTION_ENDPOINT` | - | URL string | Endpoint for token introspection/validation. |\n| - | `OAUTH_CLIENT_ID` | - | String | OAuth client ID for auth configuration. |\n| - | `OAUTH_CLIENT_SECRET` | - | String | OAuth client secret for auth configuration. |\n| - | `JWT_AUDIENCE` | - | String | Expected token audience. Falls back to `OAUTH_CLIENT_ID` if set. |\n| - | `AUTH_ISSUER`, `JWT_ISSUER` | `\"\"` | URL string | Identifier URL for the Authorization Server advertised in metadata discovery. |\n| - | `AUTH_SUB_JSONPATH` | `$.sub` | JSONPath | Payload path to extract unique user identity from JWT payload. |\n| `--auth-scope-jsonpath` | `AUTH_SCOPE_JSONPATH` | `$.scope` | JSONPath | Payload path to extract scopes claim from JWT payload. |\n| `--auth-roles-jsonpath` | `AUTH_ROLES_JSONPATH`, `AUTH_ADMIN_JSONPATH` | `$.realm_access.roles` | JSONPath | Payload path to extract user roles from JWT payload. |\n| - | `AUTH_ADMIN_ROLE` | `admin` | String | Role name signifying administrator access. |\n| `--auth-required-read-scope` | `AUTH_REQUIRED_READ_SCOPE` | `nogoo9:read` | String | OAuth scope required for read operations. If not set, read scope check is bypassed. |\n| `--auth-required-write-scope` | `AUTH_REQUIRED_WRITE_SCOPE` | `nogoo9:write` | String | OAuth scope required for write/mutation operations. If not set, write scope check is bypassed. |\n| `--auth-required-admin-scope` | `AUTH_REQUIRED_ADMIN_SCOPE` | `nogoo9:admin` | String | OAuth scope required for administrator operations. If not set, admin scope check is bypassed. |\n| - | `AUTH_ADMIN_USERS` | `` | Comma-separated list of user subject IDs (sub) | Comma-separated list of user subject IDs (sub) granted admin privileges without requiring OIDC scope/role claims (workaround fallback). |\n| `--auth-required-read-role` | `AUTH_REQUIRED_READ_ROLE` | `viewer` | String | User role required for read operations. If not set, read role check is bypassed. |\n| `--auth-required-write-role` | `AUTH_REQUIRED_WRITE_ROLE` | `user` | String | User role required for write/mutation operations. If not set, write role check is bypassed. |\n| - | `PROXY_SESSION_TTL` | `1800` | Number | Session cookie expiration lifetime in seconds (sliding window duration). |\n| - | `PROXY_REFRESH_COOKIE_TTL` | `604800` | Number | Default Max-Age for the encrypted refresh token cookie (nocr_refresh). Overridden by the IdP's refresh_expires_in when available. |\n| - | `PROXY_TOKEN_COOKIE_TTL` | `86400` | Number | Default Max-Age for the access token cookie (nocr_token). Overridden by the JWT exp claim when available. |\n| - | `PROXY_SESSION_SECRET` | `\"\"` | String | HMAC secret key used to sign stateless session cookies. Falls back to `JWT_SECRET` if not configured. |\n| - | `OAUTH_SCOPES` | `openid profile email offline_access` | Space-separated scope string | OAuth scopes to request during authorization. Include 'offline_access' for refresh tokens. |\n| - | `OAUTH_AUTHORIZATION_URL` | - | URL string | Direct OAuth authorization URL. |\n| - | `OAUTH_SERVER_DISCOVERY_URL`, `OAUTH_DISCOVERY_URL` | - | URL string | Discovery URL for the OAuth server used by the backend gateway. Falls back to OAUTH_DISCOVERY_URL. |\n| - | `OAUTH_TOKEN_URL`, `OAUTH_SERVER_TOKEN_URL` | - | URL string | Direct OAuth token exchange endpoint for the backend server. |\n| - | `OAUTH_END_SESSION_URL` | - | URL string | Direct OAuth logout endpoint. |\n| `--auth-inject-workspace-jwt` | `AUTH_INJECT_WORKSPACE_JWT` | `true` | `true`, `false` | Determines if the custom 'x-workspace-jwt' header containing the raw token is injected into proxy requests. |\n| - | `AUTH_DEFAULT_ROLE` | `viewer` | String | Fallback role if the token does not provide scopes/roles. |\n| `--max-workspaces-per-user` | `MAX_WORKSPACES_PER_USER` | `0` | Number | Maximum number of concurrent active workspaces a non-admin user can own (0 for unlimited). |\n\n### 🖥️ UI & Themes Configuration\n\n| CLI Option | Environment Variable | Default | Allowed Values | Description |\n|---|---|---|---|---|\n| - | `UI_ENABLED` | `true` | `true`, `false` | Enables the embedded HTML Pod Manager UI resource. |\n| - | `THEMES_DIR` | `themes` | Path string | Local directory path containing custom CSS UI themes. |\n| - | `THEMES_CONFIGMAP` | - | String | Name of Kubernetes ConfigMap containing custom UI theme configurations. |\n| - | `DOCS_DIR` | `/app/docs (Docker) or docs/.vitepress/dist (Local)` | Path string | Base directory from which static documentation files are served. |\n| - | `OAUTH_DISCOVERY_URL` | `\"\"` | URL string | Discovery URL for the OAuth authorization server used by the UI client. |\n| - | `OAUTH_CLIENT_ID` | `\"\"` | String | OAuth client ID for UI authorization. |\n| - | `OAUTH_LOGIN_METHOD` | `redirect` | `redirect`, `popup` | Login interaction mode for UI OAuth client. |\n| - | `UI_TITLE` | `nogoo9 Pod Manager` | String | Custom title shown in the dashboard header. |\n| - | `UI_SUBTITLE` | `On-demand Kubernetes pod orchestration and agent-sandbox management without CRDs.` | String | Custom subtitle shown below the dashboard title. |\n\n\n<!-- CONFIG_TABLES_END -->\n\n---\n\n## ☸️ Kubernetes Setup & RBAC Permissions\n\nFor the `@nogoo9/no-crd` MCP server to interact with Kubernetes, it must run with appropriate RBAC permissions. Depending on your configuration, you can deploy it with **Cluster-Wide (ClusterRole)** access or **Namespace-Scoped (Role)** access.\n\n### Tool-to-Permission Mapping\nBelow is the mapping showing which Kubernetes API resources and verbs each MCP tool requires. The server dynamically checks these permissions at startup (via `SelfSubjectAccessReview`) and only registers tools that the active identity is authorized to use.\n\n<!-- PERMISSIONS_TABLE_START -->\n\n### Resource: `configmaps`\n\n| Required Verb | Associated MCP Tools | Description / Purpose |\n|---|---|---|\n| `create` | `create_template` | Save a new pod template definition as a ConfigMap. |\n| `delete` | `delete_template` | Delete a stored pod template ConfigMap. |\n| `get` | `create_pod_from_template` | Read template pod specifications stored in ConfigMaps. |\n| `update` | `update_template` | Modify metadata, annotations, or specifications of an existing template. |\n\n### Resource: `events`\n\n| Required Verb | Associated MCP Tools | Description / Purpose |\n|---|---|---|\n| `list` | `get_workspace_events` |  |\n\n### Resource: `namespaces`\n\n| Required Verb | Associated MCP Tools | Description / Purpose |\n|---|---|---|\n| `list` | `list_namespaces` | Discover namespaces in the cluster (only required in cluster access mode). |\n\n### Resource: `pods`\n\n| Required Verb | Associated MCP Tools | Description / Purpose |\n|---|---|---|\n| `create` | `create_pod`, `create_pod_from_template`, `spawn_workspace`, `upgrade_all_workspaces`, `upgrade_workspace` | Provision and deploy new pods or workspace sandboxes. |\n| `delete` | `delete_pod`, `stop_workspace`, `upgrade_all_workspaces`, `upgrade_workspace` | Terminate and clean up pods or workspace sandboxes. |\n| `get` | `get_pod`, `get_workspace`, `upgrade_workspace` | Retrieve detailed JSON spec for a specific pod. |\n| `list` | `list_pods`, `list_workspaces`, `upgrade_all_workspaces` | Retrieve lists of pods or agent workspace pods. |\n| `patch` | `patch_pod` | Strategic merge patch labels, annotations, or resource requests/limits. |\n\n### Resource: `pods/log`\n\n| Required Verb | Associated MCP Tools | Description / Purpose |\n|---|---|---|\n| `get` | `get_pod_logs` | Retrieve standard output/error logs from pod containers. |\n\n\n<!-- PERMISSIONS_TABLE_END -->\n\n### 1. Cluster-Wide Mode (`MODE=cluster`)\nUse this mode if you want the MCP server to manage sandboxes across any namespace in the cluster.\n\nCreate a `ClusterRole` and `ClusterRoleBinding`:\n\n```yaml\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n  name: nogoo-mcp\nrules:\n  - apiGroups: [\"\"]\n    resources: [\"pods\"]\n    verbs: [\"get\", \"list\", \"watch\", \"create\", \"delete\", \"patch\", \"update\"]\n  - apiGroups: [\"\"]\n    resources: [\"pods/log\"]\n    verbs: [\"get\"]\n  - apiGroups: [\"\"]\n    resources: [\"namespaces\"]\n    verbs: [\"get\", \"list\"]\n  - apiGroups: [\"\"]\n    resources: [\"configmaps\"]\n    verbs: [\"get\", \"list\", \"create\", \"update\", \"patch\", \"delete\"]\n  - apiGroups: [\"\"]\n    resources: [\"serviceaccounts\"]\n    verbs: [\"get\", \"list\", \"create\", \"update\", \"patch\", \"delete\"]\n---\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRoleBinding\nmetadata:\n  name: nogoo-mcp\nroleRef:\n  apiGroup: rbac.authorization.k8s.io\n  kind: ClusterRole\n  name: nogoo-mcp\nsubjects:\n  - kind: ServiceAccount\n    name: nogoo-mcp\n    namespace: nogoo9 # Change to the namespace where your MCP server runs\n```\n\n### 2. Namespace-Scoped Mode (`MODE=namespaced`)\nUse this mode if the MCP server should be restricted to a single namespace (e.g. `nogoo9`). In this mode, no cluster-level or administrative permissions are needed.\n\nCreate a `Role` and `RoleBinding` in the target namespace:\n\n```yaml\napiVersion: rbac.authorization.k8s.io/v1\nkind: Role\nmetadata:\n  name: nogoo-mcp\n  namespace: nogoo9\nrules:\n  - apiGroups: [\"\"]\n    resources: [\"pods\"]\n    verbs: [\"get\", \"list\", \"watch\", \"create\", \"delete\", \"patch\", \"update\"]\n  - apiGroups: [\"\"]\n    resources: [\"pods/log\"]\n    verbs: [\"get\"]\n  - apiGroups: [\"\"]\n    resources: [\"configmaps\"]\n    verbs: [\"get\", \"list\", \"create\", \"update\", \"patch\", \"delete\"]\n  - apiGroups: [\"\"]\n    resources: [\"serviceaccounts\"]\n    verbs: [\"get\", \"list\", \"create\", \"update\", \"patch\", \"delete\"]\n---\napiVersion: rbac.authorization.k8s.io/v1\nkind: RoleBinding\nmetadata:\n  name: nogoo-mcp\n  namespace: nogoo9\nroleRef:\n  apiGroup: rbac.authorization.k8s.io\n  kind: Role\n  name: nogoo-mcp\nsubjects:\n  - kind: ServiceAccount\n    name: nogoo-mcp\n    namespace: nogoo9\n```\n*(Note: In namespace-scoped mode, the `list_namespaces` tool will only return the target namespace, and namespace parameter inputs to all tools will default to the target namespace.)*\n\n---\n\n## 📑 Workspace Templates & Spawner Annotations\n\nTemplates in `@nogoo9/no-crd` can be loaded from **three sources** (highest to lowest priority):\n\n1. **Kubernetes ConfigMaps** — labeled with `nogoo9/pod-template: \"true\"` (original mechanism)\n2. **Custom local directory** — set via `TEMPLATES_DIR` env var (YAML or JSON files)\n3. **Built-in templates** — shipped with the npm package (disable with `BUILTIN_TEMPLATES=false`)\n\nSee [ADR-001](docs/decisions/ADR-001-template-file-format.md) for format details.\n\n### 1. How to Define a Template\n\nTo register a template with the spawner, create a `ConfigMap` meeting the following requirements:\n1. **Discovery Label**: Must be labeled with `nogoo9/pod-template: \"true\"`.\n2. **Spec Key**: The `data` block must contain a key named `spec` whose value is a JSON string conforming to the `PodSpecSchema` (e.g. `containers`, `volumes`, `restartPolicy`).\n3. **Behavior Customization**: Set `annotations` on the `ConfigMap` metadata to configure advanced spawner integrations (like IAM role binding, init containers, and pre-stop lifecycle hooks).\n\n#### Example Template Definition:\n```yaml\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: node-workspace-template\n  namespace: nogoo9\n  labels:\n    nogoo9/pod-template: \"true\"\n  annotations:\n    nogoo9/description: \"A standard Node.js development sandbox with S3 storage sync\"\n    nogoo9/tag: \"node-20\"\n    nogoo9/required-context: \"PROJECT_NAME,REPO_URL\"\n    nogoo9/iam-role-arn: \"arn:aws:iam::123456789012:role/workspace-s3-access\"\n    nogoo9/init-image: \"alpine/git\"\n    nogoo9/init-command: \"git clone $REPO_URL /workspace/$PROJECT_NAME\"\n    nogoo9/pre-stop-command: \"aws s3 sync /workspace s3://my-workspace-backups/$PROJECT_NAME\"\n    nogoo9/default-grace-period: \"120\"\ndata:\n  spec: |\n    {\n      \"containers\": [\n        {\n          \"name\": \"workspace\",\n          \"image\": \"node:20-alpine\",\n          \"command\": [\"sleep\", \"infinity\"],\n          \"volumeMounts\": [\n            {\n              \"name\": \"workspace-storage\",\n              \"mountPath\": \"/workspace\"\n            }\n          ]\n        }\n      ],\n      \"volumes\": [\n        {\n          \"name\": \"workspace-storage\",\n          \"emptyDir\": {}\n        }\n      ]\n    }\n```\n\n### 2. Supported Spawner Annotations\n\nThe spawner inspects `ConfigMap` metadata annotations (and custom inline annotations passed during `spawn_workspace`) to customize the workspace lifecycle:\n\n<!-- TEMPLATE_ANNOTATIONS_TABLE_START -->\n\n| Annotation / Label Key | Type | Description |\n|---|---|---|\n| `nogoo9/workspace-auth-mode` | Annotation (Comma-separated) | Configures authorization modes for the workspace proxy. Comma-separated list of: `token-api` (exposes token retrieval endpoints at `_auth/token` & `_auth/authorize` for SPAs), `inject-headers` (rewrites headers to forward user identity like `x-user-sub` and JWTs to container; enabled by default if `AUTH_ENABLED=true`), `no-auth` (bypasses all auth/owner checks for public workspaces). *(Available from v0.6.0, no-auth from v0.8.0)* |\n| `nogoo9/auth-require-token` | Annotation (\"true\" | \"false\") | Determines if the workspace strictly requires a valid, raw OIDC access/refresh token to function. If set to `true`, session cookie authentication via `nocr_sess` alone is treated as unauthorized for routing proxy access, forcing a redirection to the dashboard login page to authenticate and retrieve a new JWT token. *(Available from v0.9.0)* |\n| `nogoo9/template-version` | Annotation (String) | Specifies the version of the pod template. Used to track if workspaces are outdated. *(Available from v0.8.0)* |\n| `nogoo9/workspace-name` | Annotation (String) | Stores the user-defined display name of the workspace. *(Available from v0.4.0)* |\n| `nogoo9/template-ref` | Annotation (String) | The reference to the pod template used to spawn the workspace (e.g. `default/workspace-terminal`). *(Available from v0.4.0)* |\n| `nogoo9/managed-by` | Label (String) | Used to label workspace pods created by this MCP server to restrict operational scope. *(Available from v0.5.0)* |\n| `nogoo9/pod-template` | Label (`\"true\"`) | Identifies a Kubernetes `ConfigMap` as a reusable pod template. |\n| `nogoo9/type` | Label (`\"workspace\"`) | Applied automatically by the spawner to identify running agent workspace pods. |\n| `nogoo9/workspace-id` | Label | Identifies the unique agent session / workspace ID associated with the running pod. |\n| `nogoo9/user-sub` | Label / Annotation | Represents the authenticated user subject (owner) of the workspace pod, used for access control validation and ServiceAccount labeling. |\n| `nogoo9/description` | Annotation (String) | A friendly, human-readable summary of the template's purpose and contents. |\n| `nogoo9/tag` | Annotation (String) | A version or tag associated with the template environment (e.g. `node-20`). |\n| `nogoo9/required-context` | Annotation (Comma-separated) | Validates that target environment variables are provided in the tool call's `context` parameter (e.g. `GITHUB_TOKEN,DATABASE_URL`). |\n| `nogoo9/iam-role-arn` | Annotation (AWS Role ARN) | Instructs the spawner to provision a dedicated Kubernetes `ServiceAccount` annotated for EKS IAM Role mapping (IRSA). |\n| `nogoo9/init-image` | Annotation (Image string) | The container image to run in the dynamic `spawner-init` init-container. |\n| `nogoo9/init-command` | Annotation (Shell command) | The shell command to run in the init-container. It automatically shares the main container's volume mounts. |\n| `nogoo9/init-share-volumes` | Annotation (\"true\" | \"false\") | Determines if the dynamic init-container shares the main container's volume mounts. Defaults to `true`. *(Available from v0.5.5)* |\n| `nogoo9/pre-stop-command` | Annotation (Shell command) | A shell command executed in a Kubernetes `preStop` lifecycle exec hook when the workspace is terminated (e.g. to save/push state). |\n| `nogoo9/pre-stop-sidecar-image` | Annotation (Image string) | If specified alongside `pre-stop-command`, runs the pre-stop command inside a dedicated sidecar container instead of the main container. |\n| `nogoo9/default-grace-period` | Annotation (Number in seconds) | Overrides the Pod's `terminationGracePeriodSeconds` (defaults to `60` if a pre-stop command is defined) to give cleanup commands time to finish. |\n| `nogoo9/workspace-port` | Annotation (Number) | The port inside the container to proxy traffic to. Defaults to `DEFAULT_WORKSPACE_PORT` or `3000`. |\n| `nogoo9/workspace-path` | Annotation (String) | The default URL subpath mapping for the workspace web interface (defaults to `/`). |\n| `nogoo9/workspace-type` | Annotation (String) | The format specification of the main entry point (e.g. `iframe`, `novnc`). |\n| `nogoo9/preview-path` | Annotation (String) | The default folder or file subpath to render in the UI files preview tab. |\n| `nogoo9/preview-type` | Annotation (String) | Fallback preview rendering mode for the preview tab (e.g. `markdown`, `html`). |\n| `nogoo9/api.<api-name>.port` | Annotation (Number) | Defines an additional HTTP service port exposed by the workspace. |\n| `nogoo9/api.<api-name>.visibility` | Annotation (String) | Specifies access visibility for the custom API endpoint. Supported values: `private` (accessible only by the workspace owner), `internal` (accessible by any logged-in user), `admin` (accessible by the owner or users with the admin scope and role), `scope:<scope_name>` (accessible by users possessing the specified OIDC scope), `role:<role_name>` (accessible by users possessing the specified user role), or a comma-separated list of allowed user subjects. |\n| `nogoo9/api.<api-name>.path` | Annotation (String) | Defines the subpath routing prefix for this specific API (e.g. `/terminal`). |\n| `nogoo9/api.<api-name>.desc` | Annotation (String) | A short description of this additional API, shown in the UI interface. |\n| `nogoo9/api.<api-name>.method` | Annotation (String) | Comma-separated list of supported HTTP methods (e.g. `GET,POST`, `*`, defaults to any method). |\n| `nogoo9/api.<api-name>.refresh` | Annotation (Duration) | Sets the refresh frequency for custom stats/activity or other mini API views in the dashboard cards (e.g. `10s`, `1m`, or `init` to query only once on startup). |\n| `nogoo9/api.stats.refresh` | Annotation (Duration) | Explicitly configures the reload frequency for the reserved `stats` API metrics on the workspace dashboard card (e.g., `10s`, `30s`, `init`). |\n| `nogoo9/api.last_activity.refresh` | Annotation (Duration) | Explicitly configures the reload frequency for the reserved `last_activity` epoch timestamp API on the workspace dashboard card (e.g., `30s`, `1m`, `init`). |\n| `nogoo9/allowed-roles` | Annotation (Comma-separated) | Comma-separated list of user roles allowed to spawn workspaces from this template (e.g., `developer,lead-dev`). Admins always bypass role restrictions. |\n| `nogoo9/allowed-scopes` | Annotation (Comma-separated) | Comma-separated list of OAuth scopes allowed to spawn workspaces from this template (e.g., `nogoo9:write`). Admins always bypass scope restrictions. |\n\n<!-- TEMPLATE_ANNOTATIONS_TABLE_END -->\n\n\n---\n\n## 📦 Programmatic SDK & API Proxy Services\n\n`@nogoo9/no-crd` provides a complete programmatic SDK and dynamic cluster routing proxy to allow developers to build custom pod orchestrators and route workspace traffic natively.\n\n### 1. Composable Programmatic SDK\n\nYou can import `@nogoo9/no-crd` in your Bun, Deno, or Node.js codebase to control pod sandboxes and templates programmatically, bypassing the MCP HTTP server.\n\n```typescript\nimport { KubeConfig } from \"@kubernetes/client-node\";\nimport { \n  initK8sContext, \n  spawnWorkspace, \n  stopWorkspace, \n  listWorkspaces \n} from \"@nogoo9/no-crd\";\n\n// 1. Initialize Kubernetes API Context (optionally pass custom configuration)\nconst kc = new KubeConfig();\nkc.loadFromDefault();\nconst ctx = initK8sContext(kc);\n\n// 2. Spawn a workspace sandbox from a template\nconst spawnResult = await spawnWorkspace(ctx, {\n  id: \"agent-session-42\",\n  templateRef: \"nogoo9/default-agent-workspace\",\n  context: {\n    \"S3_BUCKET\": \"my-bucket\",\n    \"S3_FOLDER\": \"session-42\"\n  }\n});\nconsole.log(`Spawned pod: ${spawnResult.podName}`);\n\n// 3. List active workspaces running in the namespace\nconst list = await listWorkspaces(ctx, {\n  namespace: \"nogoo9\"\n});\nconsole.log(`Active workspaces count: ${list.workspaces.length}`);\n\n// 4. Terminate the workspace sandbox\nawait stopWorkspace(ctx, {\n  id: \"agent-session-42\"\n});\n```\n\n### 2. Workspace Routing Proxy (Experimental)\n\n> [!WARNING]\n> The workspace routing proxy and JWT authentication engine are experimental and likely to change in the next version.\n\nThe server includes a built-in reverse proxy routing service. HTTP requests targeting:\n`http://<mcp-server-host>/route/<workspace-id>/<subpath>`\nare dynamically proxied directly to the running workspace pod's IP address inside the cluster.\n\nIf `AUTH_ENABLED` is true:\n- **Token Bootstrapping & Path-Scoped Cookies**: Since standard browser navigations (links, iframes) cannot transmit custom HTTP headers, the client passes the active JWT via a `?token=` query parameter on initial redirect. The proxy validates it and issues a secure, path-scoped cookie (`nocr_token`), allowing subsequent resource requests (JS, CSS, images, WebSockets) to authenticate seamlessly without URL parameters.\n- **Direct Access Redirect (SSO)**: Accessing a workspace URL directly without active credentials redirects you to the main dashboard's login screen. Once logged in, the dashboard automatically routes you back to the target workspace URL with a fresh bootstrap token.\n- **Resource Ownership Verification**: The workspace pod's `nogoo9/user-sub` label must match the JWT's subject claim, preventing unauthorized access to other users' workspaces.\n- **Proxy target port**: Inside the workspace pod defaults to `3000` or can be overridden via pod annotation `nogoo9/workspace-port` or the `DEFAULT_WORKSPACE_PORT` environment variable.\n- **Multi-Port / Custom API Routing**: You can expose and route additional APIs inside the pod (e.g., a web terminal or secondary service) by defining custom annotations in the template (e.g., `nogoo9/api.terminal.port: \"7681\"`, `nogoo9/api.terminal.path: \"/terminal\"`). The proxy will dynamically handle subpath routing and method checks.\n- A **stateless signed session cookie** (`nocr_sess`) is minted on first successful JWT validation, enabling workspace traffic to survive short-lived token expiry. *(Available from v0.4.0)* See [ADR-002](docs/decisions/ADR-002-stateless-session-cookies.md) and [ADR-003](docs/decisions/ADR-003-peer-discovery-session-key.md) for design details.\n\nFor a detailed breakdown of the redirection lifecycle, Keycloak configuration, and JWT claims, see the [SSO & OIDC Integration Guide](docs/deploy/sso-identity.md).\n\n### 3. OAuth Resource Discovery (RFC 9728)\n\nExposes the standardized metadata endpoint `GET /.well-known/oauth-protected-resource` returning:\n- Supported authorization servers.\n- Token format specifications.\n- Required scopes.\n\nThis allows client interfaces (and MCP clients) to automatically discover security requirements and handle dynamic OAuth authentication flows.\n\n### 4. Embedded Web UI & Dashboard Themes\n\nWhen the server runs in HTTP/SSE transport mode, the visual **React Pod Manager UI Dashboard** is served directly at root `/` (e.g. `http://localhost:3000/`).\n- **Dashboard Themes**: The UI includes a system/light/dark toggle and supports custom visual themes.\n- **Three-Source Theme Merge Engine**: CSS stylesheets are dynamically scanned and merged from:\n  1. **Kubernetes ConfigMap** (`THEMES_CONFIGMAP` environment variable).\n  2. **Custom Local Directory** (`THEMES_DIR` environment variable, defaults to `themes/`).\n  3. **Built-In Catalog** (pre-baked styles: Dracula, Nord, Stripe, Slack, Vercel, Apple, Superhuman, Notion, and Antigravity).\n\nDuplicate theme IDs are resolved according to priority: ConfigMap > Local Directory > Built-In Catalog. For detailed customization guidelines and CSS templates, see the [Dashboard Themes & Branding Guide](docs/deploy/themes-branding.md).\n\n---\n\n## 🔌 API Reference (MCP Tools & Resources)\n\n### Pod Tools\n- **`list_pods`**: Retrieve a summary of pods in the namespace. Filters by `labelSelector`, `fieldSelector`, and `limit`.\n- **`get_pod`**: Fetch full Kubernetes API JSON payload for a target pod name.\n- **`create_pod`**: Create a custom pod with direct container/volume specifications.\n- **`patch_pod`**: Apply a Strategic Merge Patch to modify labels, annotations, or container resource limits dynamically.\n- **`delete_pod`**: Terminate a pod with optional `gracePeriodSeconds`.\n- **`get_pod_logs`**: Fetch logs for a container with options like `tailLines`, `sinceSeconds`, `timestamps`, `limitBytes`, and `previous`.\n- **`list_namespaces`**: List all namespaces accessible with current credentials.\n- **`list_registry_images`**: List catalog images from the configured `REGISTRY_URL`.\n\n### Pod Template Tools\nManage preconfigured pod specifications stored as standard Kubernetes ConfigMaps (labeled `nogoo9/pod-template=true`).\n- **`list_templates`**: Show available templates.\n- **`get_template`**: Get the raw pod template spec.\n- **`create_template`**: Store a new pod template spec.\n- **`update_template`**: Update labels, annotations, or specs on an existing template.\n- **`delete_template`**: Delete a template.\n- **`create_pod_from_template`**: Spawn a pod using a template, applying container overrides (environment variables, commands, resources) and top-level overrides.\n\n### Agent Workspace (Spawner) Tools\nSpecially designed for AI agents to safely spawn and clean up their own workspace sandboxes.\n- **`list_workspaces`**: List active agent workspaces (with JWT/owner mapping support).\n- **`spawn_workspace`**: Spawn a workspace sandbox pod. Features:\n  - **Context Validation (`nogoo9/required-context`)**: Requires the caller to supply critical env variables (e.g. API keys) before spawning.\n  - **Init Containers (`nogoo9/init-image` / `nogoo9/init-command`)**: Initialize workspace directories/files before main containers start.\n  - **Pre-Stop Hooks (`nogoo9/pre-stop-command`)**: Run custom cleanup commands (e.g., commit/sync code to git or S3) upon termination.\n  - **IAM Role Mapping (`nogoo9/iam-role-arn`)**: Dynamically provisions AWS EKS IAM Role service accounts.\n- **`stop_workspace`**: Clean up and terminate the workspace pod.\n\n### Utilities\n- **`current_namespace`**: Returns active namespace and access mode.\n\n### MCP Resources\n- **`pod-template://{namespace}/{name}`**: Exposes stored pod templates directly as read-only MCP resources.\n- **`ui://nogoo9/app`**: Exposes the embedded React/web UI app (if `UI_ENABLED=true` and built). When the server runs in HTTP/SSE transport mode, the UI is also served directly at `/` or `/ui` (e.g. `http://localhost:3000/`) and automatically falls back to standard HTTP JSON-RPC calls when loaded outside a postMessage-compatible MCP host (such as in a standard browser tab or the MCP Inspector).\n\n---\n\n## 🏗️ Architecture\n\n```\n  ┌───────────────────────┐\n  │   AI Agent / Client   │\n  └───────────┬───────────┘\n              │ (Stdio or SSE Transport)\n              ▼\n  ┌───────────────────────┐\n  │      MCP Server       │ <── (Queries ConfigMaps for specs)\n  └───────────┬───────────┘\n              │ (Kubernetes API - CoreV1)\n              ▼\n  ┌──────────────────────────────────────────┐\n  │            Kubernetes Cluster            │\n  │  ┌────────────────────────────────────┐  │\n  │  │         Target Namespace           │  │\n  │  │  ┌──────────┐ ┌──────────┐ ┌────┐  │  │\n  │  │  │ Agent    │ │ Custom   │ │    │  │  │\n  │  │  │ Sandbox  │ │ Workload │ │... │  │  │\n  │  │  │ Pod      │ │ Pod      │ │    │  │  │\n  │  │  └──────────┘ └──────────┘ └────┘  │  │\n  │  └────────────────────────────────────┘  │\n  └──────────────────────────────────────────┘\n```\n\nThe server interacts directly with the Kubernetes API using `@kubernetes/client-node`. By using standard `Pod` and `ConfigMap` resources, the setup is highly scalable, requires no cluster operator installs, and easily adheres to strict enterprise namespace-level security policies.\n\n---\n\n## 🛠️ Development\n\nWe use [Moon](https://moonrepo.dev/moon) for toolchain management and task running, and [Biome](https://biomejs.dev) for formatting and linting.\n\n### Prerequisites\n- Bun `1.3.11`+\n- Node.js `22.14.0`+\n- Moon `2.1.3`+\n- k3d (for local Kubernetes cluster)\n\n### Setup Environment\n```bash\n# Install dependencies\nbun install\n\n# Auto-fix code formatting and linting via Biome\nbun run format\n\n# Run TypeScript compilation checks\nbun run typecheck\n```\n\n### Running Tests\n```bash\n# Run unit tests\nmoon run mcp:test\n\n# Run full spawner workspace lifecycle tests (requires local k3d)\nbun run test:lifecycle\n```\n\n### Local Cluster Testing (k3d)\nBootstrap a local `k3d` Kubernetes cluster complete with a local registry, built-in mock S3, and Traefik:\n```bash\n# Spin up development cluster\nmoon run k3d:bootstrap\n\n# Rebuild, push, and deploy MCP server to the cluster\nmoon run mcp:deploy\n\n# Tear down the cluster\nmoon run k3d:teardown\n```\n\n---\n\n## 📄 License\n\nThis project is licensed under the Apache License 2.0. See [LICENSE](LICENSE) for details.\n\n",
  "bytes": 40771,
  "sha": "54d2157b8cc9eba057f8f99dd6527da872ff976ad444b5c509da3a251b66c3b9",
  "repo_slug": "nogoo9/no-crd",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_nogoo9_no_crd_1b2ec499/readme"
}