Problem
A user-registered service can only ever match one URL prefix, so an API
that spans more than one host needs one registration per host — and one
latchkey auth set per registration, even when a single token covers all of
them.
Fastmail is a clean example. The JMAP API lives on api.fastmail.com, blobs
({downloadUrl}) on www.fastmailusercontent.com, and accounts homed on a
regional shard use different hosts again — phl.api.fastmail.com and
phl-www.fastmailusercontent.com. One API token covers all four, but setup is:
latchkey services register fastmail --base-api-url="https://api.fastmail.com/"
latchkey services register fastmail-content --base-api-url="https://www.fastmailusercontent.com/"
latchkey services register fastmail-phl --base-api-url="https://phl.api.fastmail.com/"
latchkey services register fastmail-content-phl --base-api-url="https://phl-www.fastmailusercontent.com/"
latchkey auth set fastmail -H "Authorization: Bearer $(pbpaste)"
latchkey auth set fastmail-content -H "Authorization: Bearer $(pbpaste)"
latchkey auth set fastmail-phl -H "Authorization: Bearer $(pbpaste)"
latchkey auth set fastmail-content-phl -H "Authorization: Bearer $(pbpaste)"
Four services and four auth set calls for one credential — and all four have
to be re-run every time the token rotates.
The engine already supports what's needed
ServiceRegistry.matchesUrl handles both strings and patterns
(src/serviceRegistry.ts):
matchesUrl(service, url) {
for (const baseApiUrl of service.baseApiUrls) {
if (typeof baseApiUrl === 'string') { if (url.startsWith(baseApiUrl)) return true; }
else { if (baseApiUrl.test(url)) return true; }
}
return false;
}
Service.baseApiUrls is typed readonly (string | RegExp)[], and built-ins
already use both the array and the regex form — aws is
/^https:\/\/[^/]*\.amazonaws\.com\//, mailchimp is
/^https:\/\/[^/]+\.api\.mailchimp\.com\//.
Three places close that off for user-registered services:
services register takes --base-api-url <url>, not repeatable.
RegisteredService's constructor hardcodes this.baseApiUrls = [baseApiUrl]
— always exactly one, always a string.
RegisteredServiceEntrySchema in src/configDataStore.ts is
baseApiUrl: z.string(), so hand-editing ~/.latchkey/config.json can't
express it either.
Why there's no workaround
Prefix matching is against the whole URL, so the part that varies (the
subdomain) sits at the front of the string. The only common prefix of
https://api.fastmail.com/ and https://phl.api.fastmail.com/ is https://
— a catch-all that would match every URL and collide with every other
registered service.
Requests
Either would solve it; the first is the smaller change:
- Make
--base-api-url repeatable, and widen the stored entry to
baseApiUrls: z.array(z.string()) (accepting the old singular baseApiUrl
key on read, for compatibility).
- Accept a pattern — e.g.
--base-api-url-pattern <regex>, compiled to a
RegExp and pushed into the same baseApiUrls array the matcher already
walks.
Observed on latchkey 3.5.0.
Problem
A user-registered service can only ever match one URL prefix, so an API
that spans more than one host needs one registration per host — and one
latchkey auth setper registration, even when a single token covers all ofthem.
Fastmail is a clean example. The JMAP API lives on
api.fastmail.com, blobs(
{downloadUrl}) onwww.fastmailusercontent.com, and accounts homed on aregional shard use different hosts again —
phl.api.fastmail.comandphl-www.fastmailusercontent.com. One API token covers all four, but setup is:Four services and four
auth setcalls for one credential — and all four haveto be re-run every time the token rotates.
The engine already supports what's needed
ServiceRegistry.matchesUrlhandles both strings and patterns(
src/serviceRegistry.ts):Service.baseApiUrlsis typedreadonly (string | RegExp)[], and built-insalready use both the array and the regex form —
awsis/^https:\/\/[^/]*\.amazonaws\.com\//,mailchimpis/^https:\/\/[^/]+\.api\.mailchimp\.com\//.Three places close that off for user-registered services:
services registertakes--base-api-url <url>, not repeatable.RegisteredService's constructor hardcodesthis.baseApiUrls = [baseApiUrl]— always exactly one, always a string.
RegisteredServiceEntrySchemainsrc/configDataStore.tsisbaseApiUrl: z.string(), so hand-editing~/.latchkey/config.jsoncan'texpress it either.
Why there's no workaround
Prefix matching is against the whole URL, so the part that varies (the
subdomain) sits at the front of the string. The only common prefix of
https://api.fastmail.com/andhttps://phl.api.fastmail.com/ishttps://— a catch-all that would match every URL and collide with every other
registered service.
Requests
Either would solve it; the first is the smaller change:
--base-api-urlrepeatable, and widen the stored entry tobaseApiUrls: z.array(z.string())(accepting the old singularbaseApiUrlkey on read, for compatibility).
--base-api-url-pattern <regex>, compiled to aRegExpand pushed into the samebaseApiUrlsarray the matcher alreadywalks.
Observed on latchkey 3.5.0.