Skip to content

Capability Grants

Access to Waypoint is controlled entirely by Tailscale ACL capability grants under the name redo.com/cap/waypoint. There are no users, no roles, and no permissions stored in Waypoint itself — every authorization decision is read from the ACL on each connection.

{
"grants": [{
"src": ["group:backend"],
"dst": ["tag:waypoint"],
"cap": {
"redo.com/cap/waypoint": [{
"limits": { /* optional per-user limits */ },
"backends": { /* per-listener grants */ }
}]
}
}]
}

src and dst are standard Tailscale ACL fields. The capability payload is an array of objects; if multiple grants match, Waypoint merges the backends permissions across rules TestMergeRules_MultipleRulesMergePermissions internal/auth/auth_test.go:32 and takes the strictest values from limits. TestMergeLimits_MostRestrictiveWins internal/auth/auth_test.go:109

FieldMeaning
max_connsConcurrent connections per user
max_conn_durationHard ceiling on a single connection’s lifetime
max_bytes_per_connByte budget per connection (e.g. "10GB")

Missing fields fall back to [defaults.limits] from the server config. TestMergeLimits_FirstValueSetsBaseline internal/auth/auth_test.go:123 TestMergeLimits_ZeroDoesNotOverride internal/auth/auth_test.go:144 Bandwidth tiers stack across rules but deduplicate by period and pick the strictest byte budget per period. TestMergeLimits_BandwidthMostRestrictive internal/auth/auth_test.go:173 TestMergeLimits_BandwidthMultipleTiers_DedupByPeriod internal/auth/auth_test.go:212 See Provisioning & Defaults.

Map keyed by listener name. Empty objects mean “allowed with no per-protocol detail” (the right shape for TCP listeners).

For Postgres:

{
"backends": {
"pg-main": {
"pg": {
"databases": {
"myapp": {
"permissions": ["readwrite"],
"schemas": ["public", "app"]
},
"*": { "permissions": ["readonly"] }
}
}
}
}
}

The databases map keys are database names; * matches anything not listed explicitly. TestDatabasePermissions_WildcardMatch internal/auth/auth_test.go:333 When both an exact match and * are present, the exact match’s permissions are merged with the wildcard’s. TestDatabasePermissions_ExactAndWildcardMerge internal/auth/auth_test.go:354 Each entry has:

  • permissions — array of preset names (see below).
  • schemas — schemas to apply the presets to. Defaults to ["public"].
  • sql — optional raw SQL templates. See Raw SQL.
PresetGrants
readonlyUSAGE on schema; SELECT on all tables and sequences TestExpandPresets_Readonly internal/provision/presets_test.go:8
readwriteEverything in readonly + INSERT, UPDATE, DELETE on tables, USAGE on sequences TestExpandPresets_Readwrite internal/provision/presets_test.go:28
adminALL PRIVILEGES on tables and sequences; USAGE + CREATE on schema TestExpandPresets_Admin internal/provision/presets_test.go:52

Preset names are case-insensitive TestExpandPresets_CaseInsensitive internal/provision/presets_test.go:126 , deduplicated TestExpandPresets_NoDuplicates internal/provision/presets_test.go:136 , and validated against the known set. TestExpandPresets_InvalidPreset internal/provision/presets_test.go:96

For MongoDB, presets map to MongoDB built-in roles (read, readWrite, dbAdmin). TestExpandMongoPresets_Readonly internal/provision/mongo_test.go:78 TestExpandMongoPresets_ReadWrite internal/provision/mongo_test.go:91 TestExpandMongoPresets_Admin internal/provision/mongo_test.go:101 See MongoDB for the static-user matching rules when provision.mode = "static".

Postgres clients can downgrade their effective preset for one connection with ?waypoint_presets=readonly in the connection string. This cannot elevate beyond the grant; it only narrows. TestLimitDBPermissionsToPostgresPreset internal/proxy/preset_limit_test.go:83 See Postgres.