Skip to content

Writing a Plugin

Checklist

  1. Implement pluginrpc.Plugin (Configure, GetView, DoAction, …)
  2. Entrypoint at plugins/<name>/cmd/<name> calling pluginrpc.Serve(impl)
  3. Return ViewData tables; leave rendering to the host
  4. Register in plugins.meta.yaml
  5. Document KeePass fields exactly as Configure / *_config.go read them
  6. Prefer playground seeds over committing secrets
  7. Never call back into host secrets from inside GetView over nested RPC

Minimal shape

plugins/myplugin/
├── service.go           # Plugin impl
├── service_views.go     # ViewNav + actions + help
├── client.go            # Talks to the real system
└── cmd/myplugin/
    └── main.go          # Serve

Metadata

func (s *Service) GetMetadata() (pluginapi.PluginMetadata, error) {
    return pluginapi.PluginMetadata{
        Name:        "myplugin",
        Description: "Short description for Package Manager",
    }, nil
}

And in plugins.meta.yaml:

plugins:
  myplugin:
    description: Short description for Package Manager
    tags:
      - example

Views & actions pattern

Follow Redis / Docker:

  • viewNavBindings() → digit keys 09
  • Optional moreViewBindings() for overflow views
  • Per-view *Actions() for the actions column
  • helpSections() via pluginrpc.HelpNav(...)

Configure from KeePass

func (s *Service) Configure(req pluginrpc.ConfigureRequest) error {
    s.host = req.Settings["url"]
    s.port = req.Settings["port"]
    s.password = req.Settings["password"]
    // …
    return nil
}

Document expected fields on the plugin's docs page under KeePass.

Local testing

make plugin-myplugin   # or make all
omo secrets put myplugin/development/local --url  --attr omo

Or use omo-playground for Docker-backed dependencies.

Reference implementations

Plugin Why study it
plugins/redis/ Full views / actions / help pattern
plugins/docker/ LogsBody, compose, destructive actions
plugins/ssh/ ExternalSession (interactive shell)
plugins/k8sportforward/ Long-running local tunnels