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 AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
2 changes: 1 addition & 1 deletion cloud/hostfs_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 16 additions & 11 deletions cloud/proto/doc.go
Original file line number Diff line number Diff line change
@@ -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
23 changes: 12 additions & 11 deletions cloud/proto/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -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
Expand All @@ -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"
)

Expand Down Expand Up @@ -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
)
8 changes: 6 additions & 2 deletions cloud/proto/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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
}
Expand Down
Loading