Summary
Fly's macaroon-format tokens (FlyV1 fm2_...,fm2_...) are long enough — ~690 characters, multiple comma-joined segments — that they don't survive common copy/paste paths intact, and nothing in the flow catches a truncated paste before it's stored and used. The result looks exactly like a revoked or invalid token, so every truncation turns into a full auth-failure debugging session instead of an obvious, immediate rejection.
I understand macaroons are the deliberate design (attenuation, scoping, etc.) — this isn't "stop using macaroons," it's that the format has real, avoidable UX/integration costs that nothing currently mitigates.
Concrete repro from tonight
- Real token length, measured directly: 691 characters.
- Pasted it into a third-party tool's "Fly.io API token" field twice. Both times it silently saved a 116-character value — no format check, no length check, no error at save time.
- The stored (truncated) token then failed with a generic
401 {"error":"Authenticate: token validation error"} — indistinguishable from a genuinely revoked/banned token. Nothing in that response, or in the dashboard, suggested "this doesn't look like a complete token."
- Root cause of the truncation, once found: copying the token from a terminal — the token wraps across multiple visual lines, and a triple-click (a completely standard "select this line" gesture) grabs only the wrapped visual line under the cursor, not the full logical value. Every terminal-based copy of this token is exposed to this failure mode, silently, with no indication anything went wrong.
- Separately, once the full token was correctly in place, a different opaque failure showed up: a deploy-scoped token got
403 {"error":"unauthorized"} on an org-wide GET /v1/apps?org_slug=... listing call, while every per-app endpoint (/v1/apps/<app>/machines, etc.) worked fine with the exact same token. Reasonable behavior for a scoped token, but the error gives zero signal that this is a scope boundary rather than a bad/expired token — same generic shape as the truncation failure above.
Why this matters more than it looks
This came up while wiring an AI agent to drive Fly deploys end-to-end on a human's behalf. For a human operating a CLI, a bad paste is often visible or gets caught on the next flyctl invocation. For an automation/agent context — token stored once, read and reused programmatically — a silent truncation just sits there as a stored, "valid-looking" credential that fails unpredictably, and every failure mode (truncated, revoked, wrong-scope) currently produces the same undifferentiated 401/403. That's the actual practical cost: it makes unattended/automated use of Fly tokens meaningfully harder to build reliably, not just occasionally annoying to a human typing it once.
Impact
This is significant enough on its own that I'm moving this project off Fly.io. Not filing this to be dramatic about it — filing it because the fix seems tractable (see below) and I'd rather this get better for the next person than just leave quietly.
Ask (a few independent options, any one would help)
-
A published, minimal format check for macaroon tokens (starts with FlyV1, N comma-separated fm2_... segments, a real minimum length) that third-party tools can validate a pasted value against before saving it — right now there's no way for an integrator to know a paste is short/malformed without calling a live API and getting a generic auth error back.
-
More specific auth error messages that distinguish "this doesn't parse as a well-formed token at all" vs "well-formed but rejected/expired" vs "well-formed and valid, but out of scope for this specific call" — today 401/403 collapse all three into the same shape.
-
Offer a genuinely short, regular-sized token as an option, not just the long macaroon — e.g. a normal opaque API-key-length credential for scoped/deploy use, alongside the long macaroon for whatever the macaroon's attenuation properties are actually needed for. Don't make "hundreds of characters that break on a normal paste" the only way to get a working token; let integrators choose length/robustness vs. whatever the macaroon buys them.
Happy to provide the exact terminal/environment details that produced the truncation if useful — kept generic here since it's a repro pattern, not something specific to my setup, but this is honestly ridiculous. Spent 2 hours trying to figure out why my token wasnt working only to be shocked when my AI assistant tole me a real token is damn near 700 characters
Summary
Fly's macaroon-format tokens (
FlyV1 fm2_...,fm2_...) are long enough — ~690 characters, multiple comma-joined segments — that they don't survive common copy/paste paths intact, and nothing in the flow catches a truncated paste before it's stored and used. The result looks exactly like a revoked or invalid token, so every truncation turns into a full auth-failure debugging session instead of an obvious, immediate rejection.I understand macaroons are the deliberate design (attenuation, scoping, etc.) — this isn't "stop using macaroons," it's that the format has real, avoidable UX/integration costs that nothing currently mitigates.
Concrete repro from tonight
401 {"error":"Authenticate: token validation error"}— indistinguishable from a genuinely revoked/banned token. Nothing in that response, or in the dashboard, suggested "this doesn't look like a complete token."403 {"error":"unauthorized"}on an org-wideGET /v1/apps?org_slug=...listing call, while every per-app endpoint (/v1/apps/<app>/machines, etc.) worked fine with the exact same token. Reasonable behavior for a scoped token, but the error gives zero signal that this is a scope boundary rather than a bad/expired token — same generic shape as the truncation failure above.Why this matters more than it looks
This came up while wiring an AI agent to drive Fly deploys end-to-end on a human's behalf. For a human operating a CLI, a bad paste is often visible or gets caught on the next
flyctlinvocation. For an automation/agent context — token stored once, read and reused programmatically — a silent truncation just sits there as a stored, "valid-looking" credential that fails unpredictably, and every failure mode (truncated, revoked, wrong-scope) currently produces the same undifferentiated 401/403. That's the actual practical cost: it makes unattended/automated use of Fly tokens meaningfully harder to build reliably, not just occasionally annoying to a human typing it once.Impact
This is significant enough on its own that I'm moving this project off Fly.io. Not filing this to be dramatic about it — filing it because the fix seems tractable (see below) and I'd rather this get better for the next person than just leave quietly.
Ask (a few independent options, any one would help)
A published, minimal format check for macaroon tokens (starts with
FlyV1, N comma-separatedfm2_...segments, a real minimum length) that third-party tools can validate a pasted value against before saving it — right now there's no way for an integrator to know a paste is short/malformed without calling a live API and getting a generic auth error back.More specific auth error messages that distinguish "this doesn't parse as a well-formed token at all" vs "well-formed but rejected/expired" vs "well-formed and valid, but out of scope for this specific call" — today
401/403collapse all three into the same shape.Offer a genuinely short, regular-sized token as an option, not just the long macaroon — e.g. a normal opaque API-key-length credential for scoped/deploy use, alongside the long macaroon for whatever the macaroon's attenuation properties are actually needed for. Don't make "hundreds of characters that break on a normal paste" the only way to get a working token; let integrators choose length/robustness vs. whatever the macaroon buys them.
Happy to provide the exact terminal/environment details that produced the truncation if useful — kept generic here since it's a repro pattern, not something specific to my setup, but this is honestly ridiculous. Spent 2 hours trying to figure out why my token wasnt working only to be shocked when my AI assistant tole me a real token is damn near 700 characters