diff --git a/AGENTS.md b/AGENTS.md index a0f519d..0308adf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -42,3 +42,4 @@ When adding a new feature, include the relevant docs page update and at least on - Do not run tests, start the software, start the dev server, start Docker Compose, or execute migrations unless explicitly asked by the developer. - Do not read entire translation files. Make targeted reads and edits only. - Do not add yourself as a co-author in commits. +- `cloud/proto` is a verbatim copy of the DockTail Cloud wire contract, not code owned here. Never edit it on its own: every change is made identically in the control plane's copy (the source of truth) so the two stay byte-identical, and every wire addition is `omitempty` and backward-compatible. Keep it stdlib-only, and never add exec, deploy, shell, or command message types. diff --git a/cloud/hostfs_linux.go b/cloud/hostfs_linux.go index b37c14b..91ad606 100644 --- a/cloud/hostfs_linux.go +++ b/cloud/hostfs_linux.go @@ -121,7 +121,7 @@ func resolveHostRoot() string { if root == "" || root == "/" { return "" } - if _, err := os.Stat(filepath.Join(root, "proc", "1", "mounts")); err != nil { + if _, err := os.Stat(filepath.Join(root, "proc", "1", "mounts")); err != nil { //nolint:gosec // G703: the operator names the host root via DOCKTAIL_HOST_ROOT on purpose; only stat-ed return "" } return root diff --git a/cloud/proto/doc.go b/cloud/proto/doc.go index 74a99bc..395b11e 100644 --- a/cloud/proto/doc.go +++ b/cloud/proto/doc.go @@ -1,17 +1,22 @@ -// Package proto defines the wire contract between the DockTail agent (this repo, -// AGPL) and DockTail Cloud's proprietary agent-plane. +// Package proto defines the wire contract between the DockTail agent (OSS, +// AGPL, github.com/marvinvr/docktail) and the DockTail Cloud agent-plane +// (proprietary). // // It is intentionally dependency-free (stdlib only) so it stays a clean // licensing firewall: the AGPL agent and the proprietary cloud both import an -// identical, neutral set of types without either contaminating the other. +// identical, neutral set of types without either contaminating the other. The +// intended end-state is a standalone Apache-2.0 module +// (github.com/marvinvr/docktail-proto) that both sides import. // -// NOTE: this is currently a verbatim copy of docktail-cloud/proto, kept in sync -// by hand. The intended end-state (see that repo's PLAN.md) is a single shared -// Apache-2.0 module (github.com/marvinvr/docktail-proto) that both sides import; -// until that module exists, the two copies MUST be kept byte-identical on the -// wire (same JSON field names and message shapes). +// NOTE: until that module exists, this package lives as two hand-synced copies +// — proto/ in the DockTail Cloud repo (the source of truth) and cloud/proto in +// the agent repo — that MUST stay byte-identical. Every change lands in both, +// and every field added to the wire is omitempty and backward-compatible. The +// cloud's CI diffs its copy against a pinned agent commit. // -// Transport: outbound-only WSS, JSON messages, 30s heartbeat, jittered -// reconnect. The protocol is metadata-only — there are no exec, deploy, or shell -// message types, by design and verifiable in this open source. +// Transport: outbound-only WSS, JSON messages, 30s heartbeat (doubles as +// liveness), jittered reconnect. Every frame is an [Envelope] carrying a +// typed payload. The protocol is metadata-only: there are no exec, deploy, +// or shell message types, on purpose — the non-goals are enforced +// structurally and verifiable in the open agent source. package proto diff --git a/cloud/proto/messages.go b/cloud/proto/messages.go index d04f45d..bb431a9 100644 --- a/cloud/proto/messages.go +++ b/cloud/proto/messages.go @@ -537,9 +537,9 @@ const ( // the customer's own Tailscale API quota from a buggy or hostile cloud. MinTailnetProbeIntervalMS int64 = 60_000 // DefaultTailnetProbeIntervalMS is the cloud's polling cadence per - // (workspace, tailnet). Service state changes on human timescales (an admin - // approving a service), so this is deliberately slow. - DefaultTailnetProbeIntervalMS int64 = 300_000 + // (workspace, tailnet). Only locally-up, container-backed service names are + // included in each sweep, keeping the API load bounded at this cadence. + DefaultTailnetProbeIntervalMS int64 = 120_000 ) // --------------------------------------------------------------------------- @@ -573,11 +573,12 @@ const ( ClassServiceMissing = "service_missing" // no such service definition in the tailnet at all // Public-vantage classes. The cloud produces these from its own HTTPS probe of - // a Funnel exposure (see docs/public-vantage.md). They are namespaced rather - // than reusing the transport classes above because the classification is also - // the incident's, and a bare `timeout` could not say WHICH layer timed out: - // the recovery rules ask "is this outage currently explained by the public - // vantage?" and must never mistake a local probe failure for a Funnel one. + // a Funnel exposure (see DockTail Cloud docs/vantages.md). They are + // namespaced rather than reusing the transport classes above because the + // classification is also the incident's, and a bare `timeout` could not say + // WHICH layer timed out: the recovery rules ask "is this outage currently + // explained by the public vantage?" and must never mistake a local probe + // failure for a Funnel one. ClassPublicDNS = "public_dns" // the funnel hostname does not resolve to a public address ClassPublicTimeout = "public_timeout" // no answer from the funnel within the probe budget ClassPublicRefused = "public_refused" // the funnel ingress refused the connection @@ -586,7 +587,7 @@ const ( // Deprecated: ClassServe was emitted by the removed `tailscale serve` vantage. // Recognized so pre-existing incidents and stored rows still render; never - // produced. See docs/prober.md. + // produced. See DockTail Cloud docs/vantages.md. ClassServe = "serve" ) @@ -713,7 +714,7 @@ const MaxFilesystems = 16 // Log capture modes — the workspace default ([LogConfig.Mode]) and per-service // overrides ([LogConfig.Overrides]) both use these. const ( - LogModeIncident = "incident" // capture the tail on a down-signal event (what the cloud sends unless the workspace turned capture off) - LogModeOff = "off" // never capture; also the fail-closed value for an empty or invalid mode + LogModeIncident = "incident" // capture the tail on a down-signal event (what the cloud sends for a workspace that never set this) + LogModeOff = "off" // never capture; also the fail-closed value for an empty, invalid, or reserved mode (see [SafeLogMode]) LogModeContinuous = "continuous" // reserved: rolling capture, not yet implemented ) diff --git a/cloud/proto/validate.go b/cloud/proto/validate.go index 5c51ab8..9d611ae 100644 --- a/cloud/proto/validate.go +++ b/cloud/proto/validate.go @@ -24,7 +24,7 @@ func ValidateCheckConfig(cfg CheckConfig) error { return fmt.Errorf("invalid expected HTTP status") } if cfg.Kind == "http" { - if err := validateHTTPPath(cfg.Path); err != nil { + if err := ValidateHTTPPath(cfg.Path); err != nil { return err } } @@ -89,7 +89,11 @@ func SafeLogMode(mode string) string { return LogModeOff } -func validateHTTPPath(path string) error { +// ValidateHTTPPath bounds a relative HTTP request path. It is the one rule for +// every path the cloud puts on the wire or dials itself: the check config it +// sends an agent, and the Funnel path the public vantage requests. An empty path +// is valid and means "/". +func ValidateHTTPPath(path string) error { if path == "" { return nil }