#4275·keploy

Multi-tenant keploy-agent: per-app isolation (user-space foundation)

Author: ayush3160Created Jun 15, 2026Updated Sep 1, 2026

Summary

Make the keploy-agent multi-tenant: one agent process serving many applications concurrently, with every piece of per-app state isolated by an app/session key (config, mocks, channels, static-dedup, capture filters, mode). Today the agent is structurally single-app — the proxy session, mock manager, syncMock, clientMocks, dedup, and mode are all process-global / hardcoded to a single key.

This issue tracks the OSS user-space foundation, which is the prerequisite for both deployment models:

  • Sidecar/native — attribution via /proc ancestry (container/netns mode in v1).
  • DaemonSet (enterprise) — the eBPF capture is already multi-tenant (gate admits all target pods, each event carries kernelPid); it only needs this user-space layer to route per-app, fed by an authoritative PID→session resolver.

Root cause

The data path carries no app/session key. pkg/agent/proxy/proxy.go holds a single session/mockManager; pkg/service/agent/agent.go stores mocks at hardcoded clientMocks[uint64(0)]; pkg/agent/proxy/syncMock is a package-global singleton with one output channel; record-vs-replay mode is process-wide.

Design — one keyed user-space layer

Introduce an AppContext (per app/session key) that owns everything that is a singleton today, held in an AppRegistry keyed by a string session key. The key flows on context.Context (aligning with the context-keyed syncMock approach). Attribution is pluggable: /proc walk (native) or an authoritative resolver (daemonset). The kernel already stamps kernelPid into redirect_proxy_map; we surface it through DestInfo.Get.

Mixed modes (app A records while app B replays) fall out for free once Mode is per-app.

Phases (this issue = OSS user-space)

  • Foundation — per-app session key type + context helpers; surface KernelPid on agent.NetworkAddress (comm.go).
  • AppContext + AppRegistry — skeleton owning session/mockManager/dnsCache/recordedDNSMocks/errChannel + Resolve(kernelPid).
  • De-globalize syncMockNew() + context-keyed managers; migrate emit call sites; per-app dedup queue.
  • Proxy per-app — thread the key through Record/Mock/SetMocks/SetMocksWithWindow/GetConsumedMocks/GetMockErrors/Mapping/SetGracefulShutdown + handleConnection.
  • Service + routes per-app — un-hardcode clientMocks[0]; per-app tcChan/drain; X-Keploy-App-Id middleware; per-app capture filters.
  • Client — key AgentClient.apps by real key; stamp X-Keploy-App-Id on every request; /agent/register + /agent/deregister handshake.

Out of scope (follow-ups)

  • Enterprise per-feature isolation (time-freeze per-app paths/env, static-dedup partitioning, coverage sockets, per-app upload target).
  • DaemonSet bridge (SessionForPID resolver + per-session routing at the two capture sites).
  • eBPF host-mode multi-tenancy and nested-PID-ns cgroup_id stamping (needs BPF recompile).