Skills Hub and the local Skills Marketplace double (opt-in, dev-only)
Agents can be equipped with skills: reusable instruction packages in the Agent Skills format
(skills/<id>/SKILL.md, with optional text references). Skills for configuration-driven agents live
next to the agent definitions in
services/embabel-agent-runtime/agent-definitions/skills/.
A skill never grants tools: allowed-tools in a skill and text in instructions are ignored for tool
access. See A2A and configuration-driven agents.
The dashboard's Skills Hub can additionally list skills from external Skills Marketplace connections. That marketplace integration is an early, placeholder contract, described below.
Marketplace connections
The ai-control-service can list skills from registered "Skills Marketplace" connections
(Skills Hub settings page) via GET /api/skill-catalog, using an invented placeholder wire
contract: GET {marketplaceUrl}/skills returning a JSON array of {name, description}
objects, authenticated with Authorization: Bearer <apiKey>. No real marketplace vendor
exists yet, so this repository ships a tiny fake marketplace (nginx serving a static
skills.json) purely so a developer can click through the feature by hand. It is
test/dev-only infrastructure: it has no build context, is not part of the default
docker compose up, is not referenced by any production code path or seed data, and is not
required by any automated test (those use an in-process HTTP double instead).
Starting it
The mock is defined in a separate, never-auto-merged overlay file
(docker-compose.local-marketplace.yml, not docker-compose.override.yml). It only
starts when you explicitly add it with a second -f:
docker compose -f docker-compose.yml -f docker-compose.local-marketplace.yml up
Alternatively: ./scripts/stack-start.sh --with-local-marketplace.
A plain docker compose up (no overlay) never starts mock-skills-marketplace and never
touches the egress guard described below; the default startup path is unchanged.
SKILLS_MARKETPLACE_* environment variables
These configure the ai-control-service outbound call to any registered marketplace
(real or mock). All have safe defaults and none are required for normal operation:
| Variable | Default | Meaning |
|---|---|---|
SKILLS_MARKETPLACE_REQUEST_TIMEOUT_SECONDS | 5 | Overall per-marketplace request deadline, in seconds. |
SKILLS_MARKETPLACE_CONNECT_TIMEOUT_SECONDS | 2 | TCP connect timeout, in seconds. |
SKILLS_MARKETPLACE_MAX_RESPONSE_BYTES | 262144 | Maximum response body size (bytes) before the read is aborted. |
SKILLS_MARKETPLACE_MAX_ITEMS | 500 | Maximum number of skill items accepted from one marketplace response. |
SKILLS_MARKETPLACE_MAX_REDIRECTS | 1 | Maximum number of HTTP redirects followed. |
SKILLS_MARKETPLACE_MAX_CONNECTIONS_PER_FETCH | 25 | Maximum number of marketplace connections queried in one fetch cycle. |
SKILLS_MARKETPLACE_ALLOW_PRIVATE_EGRESS | false | Deny-by-default egress guard opt-out for loopback/RFC1918/link-local/private destinations. See the warning below. |
Trying it out against the mock
- Start the stack with the overlay (command above).
- Open the dashboard, go to the Skills Hub settings page, and register a new marketplace
connection by hand:
- Marketplace URL:
http://mock-skills-marketplace/ - API key:
dev-mock-marketplace-key(the fixed, obviously-fake dev credential the mock checks for; seeinfrastructure/mock-skills-marketplace/nginx.conf. It is never a real secret and must never be reused anywhere else)
- Marketplace URL:
- Enable the connection, then open the Skills tab. The mock's
skills.jsonfixture (infrastructure/mock-skills-marketplace/skills.json) is returned as that connection's skill catalogue. An incorrect or missing API key gets a401from the mock, which the feature reports as an authentication failure.
No connection pointing at the mock is seeded anywhere. You must register it yourself through the settings UI.
Warning: SKILLS_MARKETPLACE_ALLOW_PRIVATE_EGRESS must never be set in any deployed environment
This flag exists solely so the feature can be exercised against a marketplace that resolves
to a Docker-network private address (as the mock does). It relaxes the egress guard's
deny-by-default policy for loopback, RFC1918, link-local, CGNAT, and IPv6 ULA destinations.
It is a local-development-only credential-guard relaxation, is off by default in both the
shipped application.yml and the base docker-compose.yml, and is only ever turned on by
the opt-in overlay above. Never set it in a staging or production environment.
Even with this flag enabled, cloud-metadata addresses (for example 169.254.169.254) stay
blocked. By design, the flag never widens the guard to cover metadata endpoints, regardless
of flag state.
Known limitation: double /skills if the stored URL already ends in /skills
The feature composes the request URL by appending /skills to the stored marketplace URL.
If you register a marketplace URL that already ends in /skills (e.g.
http://example.com/skills), the actual outbound request goes to
http://example.com/skills/skills. The Skills Hub settings page's help text does not
currently warn about this. Fixing that help text is out of scope for this change; register
the marketplace's base URL without a trailing /skills segment.
Related
Synced from OpenJCockpit@main. To change this page, edit it in the product repository.