Merge pull request #73 from Molecule-AI/docs/skill-catalog-backfill-from-core-1419

docs: backfill skill-catalog, workspace-files API ref, and skills-vs-bundled-tools blog
This commit is contained in:
molecule-ai[bot] 2026-04-21 15:45:04 +00:00 committed by GitHub
commit 55e90c8f92
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 501 additions and 5 deletions

View File

@ -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.*

View File

@ -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 <your-token>"
```

View File

@ -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 <workspace-token>
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-<uuid>`)
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 <name>: ..."}` |
| `500 Internal Server Error` | Docker `CopyToContainer` failure | `{"error": "failed to write file: <docker error>"}` |
## Example
### Write a file to a workspace
```bash
curl -X PUT https://platform.example.com/workspaces/ws-abc123/files/claude.md \
-H "Authorization: Bearer <workspace-token>" \
-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 <workspace-token>" \
-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`

View File

@ -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).
- [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.

View File

@ -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` |

View File

@ -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