Run a local engine:
$ make engine
CGO_ENABLED=1 go build -trimpath -ldflags "-s -w" -o ./dist/secrets-engine ./engine/daemon
$ ./dist/secrets-engine
2025/08/12 10:20:45 engine: secrets engine starting up... (~/.cache/secrets-engine/engine.sock)
2025/08/12 10:20:45 engine: discovered builtin plugin: pass
2025/08/12 10:20:45 engine: registering plugin 'pass'...
2025/08/12 10:20:45 engine: plugin priority order
2025/08/12 10:20:45 engine: #1: pass
2025/08/12 10:20:45 engine: secrets engine readyCreate secrets in your keychain:
$ make pass
CGO_ENABLED=1 go build -trimpath -ldflags "-s -w" -o ./dist/docker-pass ./pass
$ ./dist/docker-pass set foo=bar
$ ./dist/docker-pass set baz=something
$ ./dist/docker-pass ls
baz
fooQuery secrets from the engine:
$ curl --unix-socket ~/.cache/secrets-engine/engine.sock \
-X POST http://localhost/resolver.v1.ResolverService/GetSecrets \
-H "Content-Type: application/json" -d '{"pattern": "foo"}'
{"id":"foo","value":"bar","provider":"docker-pass","version":"","error":"","createdAt":"0001-01-01T00:00:00Z","resolvedAt":"2025-08-12T08:25:06.166714Z","expiresAt":"0001-01-01T00:00:00Z"}Note
On linux the socket might be on /run/user/1000/secrets-engine/engine.sock
There are three ways to integrate with the secrets engine:
- Client integrator: Use the client to query secrets and to build business logic on top that makes use of the engine.
- Plugin author: Create plugins for an engine.
- Engine integrator: Build/run an engine yourself.
Use the client module in your project:
go get github.com/docker/secrets-engine/clientUse the client to fetch a secret:
c, err := client.New()
if err != nil {
log.Fatalf("failed to create secrets engine client: %v", err)
}
// Fetch a secret from the engine
resp, err := c.GetSecret(t.Context(), secrets.Request{ID: "my-secret"})
if err != nil {
log.Fatalf("failed fetching secret: %v", err)
}
fmt.Println(resp.Value)Use the plugin module in your project:
go get github.com/docker/secrets-engine/pluginA plugin needs to implement the Plugin interface:
var _ plugin.Plugin = &myPlugin{}
type myPlugin struct {
m sync.Mutex
secrets map[secrets.ID]string
}
func (p *myPlugin) GetSecret(_ context.Context, request secrets.Request) (secrets.Envelope, error) {
p.m.Lock()
defer p.m.Unlock()
for id, value := range p.secrets {
if request.ID == id {
return secrets.Envelope{
ID: id,
Value: []byte(value),
CreatedAt: time.Now(),
}, nil
}
}
return secrets.EnvelopeErr(request, secrets.ErrNotFound), secrets.ErrNotFound
}
func (p *myPlugin) Config() plugin.Config {
return plugin.Config{
Version: "v0.0.1",
Pattern: "*",
}
}Create your plugin binary:
package main
import (
"context"
"github.com/docker/secrets-engine/plugin"
)
func main() {
p, err := plugin.New(&myPlugin{secrets: map[secrets.ID]string{"foo": "bar"}})
if err != nil {
panic(err)
}
// Run your plugin
if err := p.Run(context.Background()); err != nil {
panic(err)
}
}TODO