7e9a271090
JOIN KEYS v1.29.0 required an admin to pre-register every machine before its agent would be spoken to. The security model was right; the workflow was not -- installing the agent should be enough to add a host. POST /api/agent/join-keys mints one credential an operator hands out. A host presenting it is enrolled automatically and immediately issued its OWN per-agent token plus the public key it must pin, delivered in the config frame. The join key is a bootstrap credential, never the host's identity, so one key stays convenient without becoming a fleet-wide skeleton key: every host remains individually revocable. DIRECTORY Collapsing the tree did nothing. applyTreeCollapse found the caret with `.tree-caret i` and returned early when absent -- Font Awesome's SVG mode rewrites <i> to <svg>, so that selector matched nothing and the early return skipped setting hideBelowDepth. State now lives on the caret button and is rotated by CSS. The Discovery Plugins delete button called deleteDiscoveryPlugin(), which was never defined. The pane also had no .actionMessage, and confirmations render into one -- without it the promise never settles, so an awaited confirmation hangs forever and the action silently never happens. Plugin instances can now be edited. DISCOVERY A fresh install presented its own five containers as unmanaged discoveries. The Docker plugin now recognises the stack's compose project and attaches each container to the service it implements. Container slugs came from the container id, which changes on recreate, so every deploy minted a new resource and orphaned the old one. DOCS /docs/discovery 404'd (no slug entry) and `agents` pointed at plugins.md, leaving docs/agents.md unreachable. Adds docs/discovery.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>