diff --git a/content/blog/2026-04-21-skills-vs-bundled-tools/index.mdx b/content/blog/2026-04-21-skills-vs-bundled-tools/index.mdx new file mode 100644 index 0000000..1704828 --- /dev/null +++ b/content/blog/2026-04-21-skills-vs-bundled-tools/index.mdx @@ -0,0 +1,105 @@ +--- +title: "Skills Over Bundled Tools: Why Composable AI Beats Platform Primitives" +date: 2026-04-21 +slug: skills-vs-bundled-tools-ai-agent-platforms +description: "Hermes v0.10.0 bundles 4 platform tools. Molecule AI installs them as skills. This piece explains why composability beats convenience for production multi-agent teams." +tags: [skills, hermes, comparison, composability, AI-agents, tutorial] +--- + +# Skills Over Bundled Tools: Why Composable AI Beats Platform Primitives + +Hermes v0.10.0 launched Tool Gateway — a set of built-in tools (web search, image generation, TTS, browser automation) available to paid Portal subscribers. If you're evaluating AI agent platforms, you might see a feature list comparison that looks like this: + +- **Hermes:** Has web search, image gen, TTS, browser automation — *out of the box* +- **Molecule AI:** Doesn't seem to have these — *requires skill installation* + +That reading is fair. It's also incomplete. + +This piece explains what "skills" actually means on Molecule AI, why composability is structurally different from bundled tools, and how the two approaches serve fundamentally different use cases. + +## What "Bundled Tools" Actually Means + +Hermes Tool Gateway ships four capabilities as platform-level primitives. When you use them, you're using the same implementation every other Hermes user uses — same models, same rate limits, same behavior. You can't swap the image generator for a different one. You can't add a new tool to the bundle without a platform update. + +**The appeal is real:** sign up, start using, no configuration. That's excellent for a personal productivity tool. + +**The limitation is structural:** you get what's shipped. Your agent's capabilities are defined by what the platform vendor decided to include, and they don't change until the vendor ships an update. + +## What "Skills" Actually Means + +Skills on Molecule AI are installable capability packages — analogous to npm packages or pip modules, but for agent capabilities. A skill packages: + +- **Tool definitions** — the JSON Schema interfaces the agent sees +- **Runtime code** — the actual implementation (API calls, local processing, etc.) +- **Configuration** — sensible defaults, required env vars, scoping rules +- **Versioning** — install a specific version, upgrade when ready + +The browser automation skill on Molecule AI isn't a different feature from Hermes' browser tool — it uses the same underlying technology (Chrome DevTools Protocol over WebSocket). The difference is *how you install it* and *what you can do with it*: + +```bash +# Install browser automation skill +molecule skills install browser-automation + +# Install TTS skill (alternative: use your own provider) +molecule skills install tts --provider openai + +# Install a community skill +molecule skills install arxiv-research --from community +``` + +After installation, your agent sees the tools the same way it sees any other tool — they're first-class in the agent's tool context. But unlike Hermes' bundled approach, you can: + +- Swap the TTS provider (OpenAI → ElevenLabs → self-hosted) +- Version-pin to a known-good skill release +- Inspect the skill code (it's just Python) +- Contribute a new or improved version back to the community +- Run the same skill locally or on any cloud VM + +## The Composability Difference + +Bundled tools are a feature set. Skills are a *package manager*. The difference matters as your agent stack grows. + +**With bundled tools:** you use what ships. If the image generator doesn't support a format you need, you file a feature request. + +**With skills:** you combine what's available. Need a specialized tool that doesn't exist? Write a skill and install it. Someone else already built it? Install it with one command. + +The skills ecosystem on Molecule AI already covers the same ground as Hermes Tool Gateway — browser automation, TTS, image generation, web search — plus dozens of additional capabilities contributed by the community. You start from zero by default, which has higher first-run friction. But the ceiling is open. + +## The Production Trust Angle + +For **individual developers**, bundled tools win on first-impression convenience. For **production teams**, the calculus is different. + +When you deploy an agent in production, you need to answer: + +- *Who has access to which tools?* (auth) +- *Which pipeline used which tool?* (audit) +- *Can I revoke access without a redeploy?* (operations) + +Hermes Tool Gateway is designed for personal Portal accounts — there's no org-level auth, no per-user scoping, no audit trail across a team. + +Molecule AI's skills run inside workspaces, which means they inherit the full access control model: + +- **Org-scoped API keys** — name, revoke, and audit every integration independently +- **Workspace-level secrets** — per-agent credential management, no shared tokens +- **Audit trail** — `org:keyId` on every request, chain of custody from minting to use + +If you're evaluating a platform for a team, the question isn't just "does it have tools?" — it's "can I trust those tools in a production context?" Molecule AI's answer to that question is the skills ecosystem *plus* the auth layer that surrounds it. + +## The Unified Narrative + +Here's how the comparison lands: + +> **Hermes** is a personal AI assistant — batteries included, no auth model, no team features. +> +> **Molecule AI** is a platform your team can build on, ship to customers, and trust in production — with skills for every capability Hermes bundles, org-level auth, and audit on every call. + +If you want to evaluate Molecule AI's skills coverage, start here: + +→ [MCP browser automation guide](/blog/browser-automation-ai-agents-mcp) — browser tools via Chrome DevTools Protocol, same capability as Hermes' built-in browser +→ [TTS and image generation skills](/docs/guides/skill-catalog) — community-contributed, versioned, swappable +→ [Org-scoped API keys](/docs/guides/org-api-keys.md) — production auth and audit + +**The tagline:** Batteries included is nice. Batteries you can trust, extend, and audit is a platform. + +--- +*Skills are how Molecule AI delivers the same capabilities as Hermes Tool Gateway — with more flexibility, more control, and a production trust model built in. Browse the full skill catalog on GitHub or install directly via the CLI.* \ No newline at end of file diff --git a/content/docs/agent-runtime/workspace-runtime.md b/content/docs/agent-runtime/workspace-runtime.md index 852fbf7..fa59694 100644 --- a/content/docs/agent-runtime/workspace-runtime.md +++ b/content/docs/agent-runtime/workspace-runtime.md @@ -149,7 +149,7 @@ External workspaces run outside the platform's Docker infrastructure — on your | Liveness | Docker health sweep | Heartbeat TTL (90s offline threshold) | | Registration | Automatic at container start | Manual: `POST /workspaces` + `POST /registry/register` | | Token | Inherited from container env | Minted at registration, shown once | -| Secrets | Baked in image or env var | Pulled from platform at boot via `GET /workspaces/:id/secrets/values` | +| Secrets | Baked in image or env var | Pulled from platform at boot via `GET /workspaces/:id/secrets` | ### Registration flow @@ -190,7 +190,7 @@ The platform returns a 256-bit bearer token — save it, it is shown only once. **3. Pull secrets at boot:** ```bash -curl http://localhost:8080/workspaces/ws-xyz/secrets/values \ +curl http://localhost:8080/workspaces/ws-xyz/secrets \ -H "Authorization: Bearer " ``` diff --git a/content/docs/api/workspace-files.mdx b/content/docs/api/workspace-files.mdx new file mode 100644 index 0000000..a5874fc --- /dev/null +++ b/content/docs/api/workspace-files.mdx @@ -0,0 +1,191 @@ +--- +title: Workspace File Copy API +description: API reference for the workspace file copy and write operations, including CWE-22 path traversal protection. +--- + +# Workspace File Copy API + +> **Source:** `workspace-server/internal/handlers/container_files.go` + `templates.go` +> **Handler:** `TemplatesHandler.WriteFile` → `copyFilesToContainer` +> **Security:** CWE-22 path traversal protection (PRs #1267, #1270, #1271) + +`copyFilesToContainer` is the internal Go implementation that powers workspace file write operations. It is not called directly by API clients — clients reach it through the HTTP handler `PUT /workspaces/:id/files/*path`. + +## Endpoint Overview + +`PUT /workspaces/:id/files/*path` writes a single file to a workspace container or its config volume. + +``` +PUT /workspaces/:id/files/*path +Authorization: Bearer +Content-Type: application/json + +{ + "content": "string" +} +``` + +The handler (`TemplatesHandler.WriteFile`) validates the path, then routes to one of two backends: + +| Workspace state | Backend | Method | +|---|---|---| +| Container running | Docker `CopyToContainer` (tar) | `copyFilesToContainer` | +| Container offline | Ephemeral Alpine container | `writeViaEphemeral` → `copyFilesToContainer` | + +Both paths use `copyFilesToContainer` internally. The ephemeral container path mounts the config volume as `/configs` and calls the same function, so CWE-22 protection applies regardless of container state. + +## Function Signature + +```go +func (h *TemplatesHandler) copyFilesToContainer( + ctx context.Context, + containerName string, + destPath string, + files map[string]string, // filename → content +) error +``` + +| Parameter | Type | Description | +|---|---|---| +| `ctx` | `context.Context` | Request-scoped context | +| `containerName` | `string` | Docker container name or ID | +| `destPath` | `string` | Target directory inside the container (typically `/configs`) | +| `files` | `map[string]string` | Map of relative filenames to file contents | + +## Parameters + +### `containerName` + +The running container for the workspace. Resolved by `TemplatesHandler.findContainer`, which checks three candidates in order: + +1. Platform provisioner naming convention (`ws-`) +2. The full workspace container ID +3. The workspace name from the database (spaces replaced with dashes) + +If the container is not running, `findContainer` returns `""` and the handler falls back to `writeViaEphemeral`. + +### `destPath` + +The directory inside the container where files are written. In normal operation this is `/configs`, which is mounted from the platform-managed config volume. All file operations are constrained to this volume. + +### `files` (`map[string]string`) + +A map of relative filenames to their string content. File names are **relative paths only** — absolute paths and `..` traversal sequences are rejected before the tar header is written. + +## Security Notes + +### CWE-22 Path Traversal Protection + +**PRs #1267, #1270, #1271** added path traversal protection at the tar-archive-write boundary. + +Before these PRs, `copyFilesToContainer` used raw map keys as tar header names without validation: + +```go +// Before — UNSAFE +header := &tar.Header{ + Name: name, // name came directly from map key + Mode: 0644, + Size: int64(len(data)), +} +``` + +A malicious caller embedding `../` in a file name could write outside the volume mount. Now: + +```go +// After — SAFE (PRs #1267 / #1270) +clean := filepath.Clean(name) +if filepath.IsAbs(clean) || strings.HasPrefix(clean, "..") { + return fmt.Errorf("unsafe file path in archive: %s", name) +} +archiveName := filepath.Join(destPath, name) +header := &tar.Header{ + Name: archiveName, // always inside destPath + Mode: 0644, + Size: int64(len(data)), +} +``` + +The validation works in three stages: + +1. **`filepath.Clean`** normalizes the path (removes redundant separators, resolves `.`). +2. **Absolute path check** (`filepath.IsAbs`) rejects any path that resolves to an absolute OS path. +3. **`..` prefix check** (`strings.HasPrefix`) rejects paths that would escape the destination via parent-directory traversal. + +The resulting `archiveName` is always inside `destPath`, so the tar header can never write outside the mounted volume regardless of input. + +> **Defense in depth:** `WriteFile` (the HTTP handler) also calls `validateRelPath(filePath)` **before** passing the path to `copyFilesToContainer`. This closes the gap for any future caller that bypasses the handler-level check. Do not remove handler-level `validateRelPath` when modifying this code. + +### Handler-Level Validation (`validateRelPath`) + +```go +func validateRelPath(relPath string) error { + clean := filepath.Clean(relPath) + if filepath.IsAbs(clean) || strings.HasPrefix(clean, "..") { + return fmt.Errorf("path traversal blocked: %s", relPath) + } + return nil +} +``` + +`validateRelPath` is called at the start of every file operation handler (`WriteFile`, `ReadFile`, `DeleteFile`, `ListFiles`). Invalid paths return `400 Bad Request` with `{"error": "invalid path"}`. + +Allowed root paths are also allow-listed: `root` must be one of `/configs`, `/workspace`, `/home`, or `/plugins`. Other values return `400 Bad Request`. + +## Error Codes + +`copyFilesToContainer` returns errors directly. The `WriteFile` HTTP handler wraps them: + +| HTTP status | Condition | Response body | +|---|---|---| +| `400 Bad Request` | `validateRelPath` rejects the path (traversal attempt) | `{"error": "invalid path"}` | +| `400 Bad Request` | Malformed JSON body | `{"error": "invalid request body"}` | +| `404 Not Found` | Workspace not found in database | `{"error": "workspace not found"}` | +| `500 Internal Server Error` | Docker unavailable | `{"error": "failed to write file: docker not available"}` | +| `500 Internal Server Error` | Tar header write failure | `{"error": "failed to write file: failed to write tar header for : ..."}` | +| `500 Internal Server Error` | Docker `CopyToContainer` failure | `{"error": "failed to write file: "}` | + +## Example + +### Write a file to a workspace + +```bash +curl -X PUT https://platform.example.com/workspaces/ws-abc123/files/claude.md \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{ + "content": "# My Agent\n\nThis agent specializes in code review.\n" + }' +``` + +**Success response (`200 OK`):** + +```json +{ + "status": "saved", + "path": "claude.md" +} +``` + +### Path traversal rejected + +```bash +curl -X PUT https://platform.example.com/workspaces/ws-abc123/files/../../etc/passwd \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{"content": "hacked"}' +``` + +**Rejection response (`400 Bad Request`):** + +```json +{ + "error": "invalid path" +} +``` + +## Related + +- [Platform API Reference](./platform-api.md) — full API endpoint table +- [Workspace Runtime](../agent-runtime/workspace-runtime.md) — runtime environment model +- `workspace-server/internal/handlers/templates.go` — `WriteFile`, `validateRelPath` +- `workspace-server/internal/handlers/container_files.go` — `copyFilesToContainer`, `writeViaEphemeral` diff --git a/content/docs/guides/index.md b/content/docs/guides/index.md index 50bcb1a..a05ef12 100644 --- a/content/docs/guides/index.md +++ b/content/docs/guides/index.md @@ -20,4 +20,8 @@ Step-by-step guides for common Molecule AI integrations and configurations. ## Authentication - [Org-Scoped API Keys](/docs/guides/org-api-keys) — Mint, audit, and revoke named API keys for organization-level access. Replace `ADMIN_TOKEN` with scoped credentials for scripts, agents, and CI. -- [Workspace Auth Tokens](/docs/architecture/workspace-auth-tokens) — Technical deep-dive on per-workspace bearer token authentication (Phase 30.1). \ No newline at end of file +- [Workspace Auth Tokens](/docs/architecture/workspace-auth-tokens) — Technical deep-dive on per-workspace bearer token authentication (Phase 30.1). + +## Skills + +- [Skill Catalog](/docs/guides/skill-catalog) — Install, manage, and publish skills for browser automation, TTS, image generation, web search, and more. Covers CLI commands, version pinning, and the skill lifecycle. diff --git a/content/docs/guides/remote-workspaces.md b/content/docs/guides/remote-workspaces.md index 742b691..e62f428 100644 --- a/content/docs/guides/remote-workspaces.md +++ b/content/docs/guides/remote-workspaces.md @@ -69,7 +69,7 @@ Laptop (remote agent) Molecule AI Platform │ POST /registry/register ────────────► │ ← admin token (one-time) │ ←─ auth_token (256-bit) ◄────────── │ ← shown once, saved to disk │ │ - │ GET /workspaces/:id/secrets/values │ ← bearer: auth_token + │ GET /workspaces/:id/secrets │ ← bearer: auth_token │ POST /registry/heartbeat (30s loop) │ │ GET /workspaces/:id/state (30s loop)│ │ │ @@ -135,7 +135,7 @@ The agent appears on the canvas with a **purple REMOTE badge** within seconds. F | Phase | What shipped | Endpoint | |---|---|---| | 30.1 | Workspace auth tokens | `POST /registry/register`, `POST /registry/heartbeat` | -| 30.2 | Token-gated secrets pull | `GET /workspaces/:id/secrets/values` | +| 30.2 | Token-gated secrets pull | `GET /workspaces/:id/secrets` | | 30.3 | Plugin tarball download (remote install) | `GET /plugins/:name/download` | | 30.4 | Workspace state polling (no WebSocket needed) | `GET /workspaces/:id/state` | | 30.5 | A2A proxy enforces caller token | `POST /workspaces/:id/a2a` | diff --git a/content/docs/guides/skill-catalog.md b/content/docs/guides/skill-catalog.md new file mode 100644 index 0000000..337becc --- /dev/null +++ b/content/docs/guides/skill-catalog.md @@ -0,0 +1,196 @@ +# Skill Catalog + +Skills extend what a workspace agent can do — from browser automation +and TTS to research tools and custom API integrations. This page covers +available skill types, how to install them, and how to manage their +versions. + +> **Note:** Molecule AI does not ship a hosted skill marketplace. All +> skills are installed from local packages, GitHub URLs, or community +> bundles. See [Skill Lifecycle](#lifecycle) for how to publish and +> distribute skills within your org. + +## Available Skill Types + +The skills ecosystem covers the same capabilities as Hermes Tool Gateway +and more: + +| Category | Skill | What it does | Provider options | +|----------|-------|-------------|-----------------| +| **Browser** | `browser-automation` | Chrome DevTools Protocol via MCP — navigate, query DOM, screenshot, fill forms. Same engine as Hermes' built-in browser tool. | Built-in (CDP); swap via skill version | +| **TTS** | `tts` | Text-to-speech generation. Streams audio to output. | OpenAI, ElevenLabs, or self-hosted | +| **Image gen** | `image-generation` | Generates images from text prompts. | OpenAI DALL·E, Stability AI, or self-hosted | +| **Web search** | `web-search` | Structured web search with result parsing. | Brave, SerpAPI, or custom | +| **Research** | `arxiv-research` | Searches and summarizes arXiv papers. | Community bundle | +| **Code** | `code-analysis` | Static analysis, diff review, complexity scoring. | Built-in | +| **SEO** | `seo-audit` | Lighthouse audit + GSC keyword extraction. | Built-in | +| **Social** | `social-post` | Formats and posts to social channels. | Built-in | + +All skills are open source. Source is visible — inspect the `SKILL.md` +and `tools/` before installing. + +## Installing a Skill + +### From the built-in catalog + +```bash +# Install browser automation +molecule skills install browser-automation + +# Install TTS with a specific provider +molecule skills install tts --provider openai + +# Install a specific version +molecule skills install browser-automation --version 1.2.0 +``` + +### From GitHub + +```bash +molecule skills install \ + https://github.com/acme/molecule-skills/tree/main/browser-automation +``` + +### From a community bundle + +Community skills are hosted on GitHub and referenced by slug: + +```bash +molecule skills install arxiv-research --from community +``` + +Community skills are reviewed by the Molecule AI team before being +listed. Submit a skill for review by opening a PR against +[`molecule-ai/skills`](https://github.com/Molecule-AI/skills). + +## Installing via config.yaml + +Skills can also be declared in the workspace config file: + +```yaml +skills: + - name: browser-automation + source: builtin + - name: tts + source: builtin + config: + provider: openai + - name: arxiv-research + source: community +``` + +On workspace boot, the runtime validates each skill and loads the +`SKILL.md` + tools into the agent's context. + +## Version Management + +Skills are versioned with semantic versioning. Pin to a known-good +release to prevent unexpected behavior changes: + +```bash +# Pin to a specific version +molecule skills install tts --version 1.1.0 + +# Upgrade to latest +molecule skills upgrade tts + +# View installed version +molecule skills list +``` + +Upgrading is safe — the skill loader validates the new package on +installation. If the new version has breaking changes, the workspace logs +a warning and keeps the previous version active until you restart. + +## Custom Skills + +Write a skill for your team's specific workflow: + +```bash +# Scaffold a new skill +molecule skills init my-custom-skill +``` + +This creates: + +``` +skills/my-custom-skill/ ++-- SKILL.md # instructions + frontmatter ++-- tools/ +| +-- my_tool.py # MCP tool using @tool decorator ++-- examples/ # few-shot examples ++-- templates/ # reference files +``` + +See [Skills Reference](../agent-runtime/skills.md) for the full +`SKILL.md` format and frontmatter schema. + +## Skill Lifecycle + +``` +Author writes SKILL.md + tools/ + | + v +Install into workspace (local or GitHub) + | + v +Workspace loads skill on next boot / hot-reload + | + v +Agent sees skill in tool context + | + v +(Optional) Publish to org bundle or community +``` + +**Publishing to your org:** Bundle skills with workspace templates so +every new workspace in a role gets the same capability set: + +```bash +molecule skills bundle my-custom-skill --output ./org-templates/my-role/ +``` + +**Publishing to the community:** Open a PR against +[`molecule-ai/skills`](https://github.com/Molecule-AI/skills) with a +complete skill package. Community skills are reviewed for security and +correctness before listing. + +## Removing a Skill + +```bash +molecule skills uninstall browser-automation +``` + +Or remove from `config.yaml` and trigger a hot-reload by touching the +file: + +```bash +touch /configs/config.yaml +``` + +The workspace detects the change, rescans skills, and updates the Agent +Card within ~3 seconds. + +## Troubleshooting + +**Skill not found:** Check the skill name matches the catalog exactly. +Skill names are lowercase with hyphens (`browser-automation`, not +`browser_automation` or `BrowserAutomation`). + +**Skill loads but tools are missing:** Verify the `tools/` folder +contains valid Python files with `@tool`-decorated functions. See +[Skills Reference — Tool Interface](../agent-runtime/skills.md#tool-interface). + +**Provider auth error:** Ensure the required environment variable (e.g. +`OPENAI_API_KEY`) is set in the workspace config or secrets. + +## Related Docs + +- [Skills Reference](../agent-runtime/skills.md) — Full SKILL.md format, + frontmatter schema, and tool interface +- [Config Format](../agent-runtime/config-format.md) — How skills are + declared in `config.yaml` +- [Plugin System](../plugins/overview.md) — Installing full plugin + packages (skills + MCP servers + shared rules) +- [Remote Agent Tutorial](../tutorials/register-remote-agent.md) — + Installing skills on remote (external) agents \ No newline at end of file