Enrich 403 register response with structured permission hint
Summary
Operator-visible failure today: a Tentacle register call against an API key that lacks `MachineCreate` returns a bare HTTP 403 with a prose message. The operator has no way to know which role to assign without reading server source.
Three coordinated changes:
Server: `PermissionRoleResolver` maps a permission to its granting built-in roles. `SquidResponse` gains optional `MissingPermission` + `SuggestedRoles` fields (additive; null on non-403). `GlobalExceptionFilter` populates them on `PermissionDeniedException`.
Tentacle: New `PermissionDeniedRegistrationException` (extends `HttpRequestException`). `TentacleRegistrationClient` parses the structured 403 body; `TentacleEntry` catches the typed exception, emits a multi-line operator hint to stderr, exits with code 403 (instead of generic 1).
Install script (PR 1): already wraps `$LASTEXITCODE -eq 403` → operator copy-pasted snippet now surfaces the hint automatically.
Pinned: `MachineCreate` → `Environment Manager + Space Owner` (System Administrator is system-level only, deliberately omitted).
Test plan
-
Unit: 5 new PermissionRoleResolver tests + 3 new TentacleRegistrationClient tests -
Backwards-compat: old server without structured fields → client falls back to generic exception (test pinned) -
WAF robustness: malformed-JSON 403 body → generic exception, no crash (test pinned) -
Build: zero errors across Squid.Core / Squid.Tentacle / Squid.Api
Depends on PR #329 (install-info.json + auto-elevation) to be in place for the install-script-side exit-code propagation to work end-to-end.