Skip to content

Conversation

@raylas
Copy link

@raylas raylas commented Feb 9, 2026

Summary

Adds a new Goa plugin that provides declarative authorization using Tailscale's app capabilities feature. This enables services running behind tailscale serve to enforce fine-grained access control based on ACL grants configured in the tailnet policy.

What it does

  • Parses the Tailscale-App-Capabilities header injected by Tailscale
  • Matches caller grants against method requirements (capability, action, resource)
  • Returns 401 for missing headers, 403 for insufficient permissions
  • Supports wildcard matching for actions and resources

DSL functions

  • Require(capability, action, resource) - declares required permissions for a method
  • AllowAnonymous() - bypasses capability checks (e.g., for health endpoints)

Files added

tscap/
├── auth/
│   ├── auth.go          # Core parsing and checking logic
│   └── auth_test.go     # Table-driven tests
├── dsl/
│   └── tscap.go         # DSL functions for Goa design files
├── generate.go          # Code generation plugin
├── example/             # Working example with generated code
└── README.md            # Usage documentation

Usage example

Method("list", func() {
    tscap.Require("example.com/cap/myapp", "read", "*")
    HTTP(func() { GET("/items") })
})

Method("health", func() {
    tscap.AllowAnonymous()
    HTTP(func() { GET("/health") })
})

Requirements

  • Tailscale 1.92+ with app capabilities
  • Service must be served via tailscale serve --accept-app-caps
  • The Tailscale Kubernetes Operator is expected to add support for app capabilities soon

Test plan

  • Unit tests for action/resource matching logic
  • Unit tests for capability parsing
  • Unit tests for authorization checks
  • Example service with generated code

@raylas raylas force-pushed the tailscaleAppCapabilities branch from 0ac8419 to 56d1c4a Compare February 9, 2026 23:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant