Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions control-plane/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ COPY policies/default.yaml /etc/agentlock/policies/default.yaml
ENV AGENTLOCK_LISTEN=0.0.0.0:7878
ENV AGENTLOCK_DASHBOARD_LISTEN=0.0.0.0:7879
ENV AGENTLOCK_HOME=/var/lib/agentlock
ENV AGENTLOCK_POLICY=/etc/agentlock/policies/default.yaml
ENV AGENTLOCK_IN_CONTAINER=1
# Default to unattested so `docker run` works zero-conf. TUI + dashboard
# show the red "UNATTESTED — LEDGER NOT SIGNED" banner. Override with
Expand Down
40 changes: 40 additions & 0 deletions control-plane/cmd/control-plane/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ func main() {
if addr == "" {
addr = "127.0.0.1:7878"
}
if len(os.Args) > 1 && os.Args[1] == "--health" {
runHealthProbe(addr)
return
}
home := os.Getenv("AGENTLOCK_HOME")
if home == "" {
log.Fatalf("AGENTLOCK_HOME is required (ledger state lives there)")
Expand Down Expand Up @@ -116,6 +120,42 @@ func main() {
log.Printf("control-plane stopped")
}

// runHealthProbe is invoked when the binary is run as `agentlockd --health`
// (e.g. from the docker HEALTHCHECK). Distroless has no curl/wget, so we
// reuse the binary itself as the probe. The listen addr may be 0.0.0.0:port;
// rewrite to 127.0.0.1 for the probe.
func runHealthProbe(listen string) {
probeAddr := listen
if h, p, ok := splitHostPort(listen); ok && (h == "" || h == "0.0.0.0" || h == "::") {
probeAddr = "127.0.0.1:" + p
}
client := &http.Client{Timeout: 1500 * time.Millisecond}
resp, err := client.Get("http://" + probeAddr + "/v1/health")
if err != nil {
log.Printf("healthcheck: %v", err)
os.Exit(1)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Printf("healthcheck: status %d", resp.StatusCode)
os.Exit(1)
}
}

func splitHostPort(addr string) (host, port string, ok bool) {
i := -1
for j := len(addr) - 1; j >= 0; j-- {
if addr[j] == ':' {
i = j
break
}
}
if i < 0 {
return "", "", false
}
return addr[:i], addr[i+1:], true
}

// loadPolicy reads $AGENTLOCK_POLICY if set; otherwise returns a built-in
// safe default (monitor mode, destructive-bash only) so the daemon always
// starts with *some* policy bound to session attestations.
Expand Down
1 change: 0 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ services:
environment:
AGENTLOCK_LISTEN: "0.0.0.0:7878"
AGENTLOCK_DASHBOARD_LISTEN: "0.0.0.0:7879"
AGENTLOCK_POLICY: "/etc/agentlock/policies/default.yaml"
AGENTLOCK_HOME: "/var/lib/agentlock"
volumes:
- agentlock-state:/var/lib/agentlock
Expand Down
6 changes: 3 additions & 3 deletions policies/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ gates:
command_regex: '^(pip|pip3|npm|pnpm|yarn|brew|cargo|gem) (install|add|-i)\b'
evaluate:
- kind: typosquat
reference: policies/lists/pkg-allowlist.txt
reference: __INLINE__:numpy,pandas,requests,flask,django,fastapi,pytest,pip,setuptools,wheel,react,vue,express,lodash,axios,typescript,eslint,prettier,jest,vite,git,node,python,go,rust,docker,kubectl
action_on_near_miss: deny
- kind: allowlist
list: policies/lists/pkg-allowlist.txt
list: __INLINE__:numpy,pandas,requests,flask,django,fastapi,pytest,pip,setuptools,wheel,react,vue,express,lodash,axios,typescript,eslint,prettier,jest,vite,git,node,python,go,rust,docker,kubectl
on_hit: allow
on_miss: deny

Expand Down Expand Up @@ -61,7 +61,7 @@ gates:
- { tool: mcp.http.request }
evaluate:
- kind: host-allowlist
list: policies/lists/net-allowlist.txt
list: __INLINE__:github.com,raw.githubusercontent.com,api.github.com,registry.npmjs.org,pypi.org,files.pythonhosted.org,crates.io,static.crates.io,rubygems.org,pkg.go.dev,proxy.golang.org,sum.golang.org
on_hit: allow
on_miss: deny

Expand Down
Loading