Skip to content

feat: add hosted-API auth and per-chain routing to clients - #325

Merged
tvinagre merged 2 commits into
mainfrom
tl/hosted-api-client-auth
Jul 21, 2026
Merged

feat: add hosted-API auth and per-chain routing to clients#325
tvinagre merged 2 commits into
mainfrom
tl/hosted-api-client-auth

Conversation

@tvinagre

@tvinagre tvinagre commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

What

Adds hosted-API authentication and per-chain request routing to both typed Fynd clients (Rust + TypeScript), so they work out-of-the-box against the hosted gateway at fynd-api.propellerheads.xyz instead of forcing users onto raw curl/fetch.

  • Rust (fynd-client): new FyndClientBuilder::with_api_key + with_chain, backed by a HostedConfig struct. The API key is sent as Authorization: Bearer ***; the chain slug rewrites request paths to /v1/{chain}/quote|health|info and derives the EVM chain_id used for signing.
  • TypeScript (@kayibal/fynd-client): new apiKey/chain/headers on FyndClientOptions. Chain routing is implemented as an openapi-fetch middleware that rewrites /v1//v1/{chain}/, so call sites stay typed against the generated schema literals. createFyndClient and the Middleware type are now re-exported from the package root (resolves the long-standing feat: implement TychoFeedBuilder for TychoFeed creation and configuration #12 export gap).

Why

The hosted gateway requires a bearer token and per-chain paths (/v1/{chain}/quote). Neither client could produce those requests.

Bundled fix: build_quote_only hardcoded chain_id: 1, so a quote-only client on a non-mainnet chain would sign transactions stamped for mainnet whenever SigningHints supplied nonce, fees, and gas. The chain ID is now derived from the chain slug, and build() cross-checks the slug against the RPC-reported chain ID.

Backward compatibility

Both options are opt-in. Unset, the clients reproduce the previous unauthenticated requests to the unprefixed /v1/… paths — self-hosted Fynd users see zero behavior change.

Supported chains

ethereum (1), base (8453), arbitrum (42161), bsc (56), polygon (137), unichain (130). Unknown slugs fail at build time with a Config error listing the accepted set.

Validation

Gate Result
cargo check --package fynd-client pass
cargo clippy --workspace --all-targets --all-features pass, 0 warnings
cargo +nightly fmt --all --check pass
cargo test -p fynd-client 93 lib + 9 integration + 5 new = all pass
TS typecheck / lint / test pass / 0 warnings / 157 pass

New tests were mutation-tested (suppressing the bearer header, middleware, and chain_id derivation failed 6 Rust + 5 TS tests; the 2 TS tests that stayed green assert absence of the new behavior, which is correct).

@github-actions

Copy link
Copy Markdown

No API Breaking Changes Detected

The PR title signals breaking changes, but cargo-semver-checks found none.
If the breaking change is behavioral, CLI, or config-level (not public Rust API), this is expected.
Otherwise, consider using fix: instead of feat: in the PR title.

@tvinagre

Copy link
Copy Markdown
Collaborator Author

Heads-up for reconciliation when this merges: PR #327 (benchmark gateway auth, branch tl/benchmark-gateway-auth, based on tl/release-0.90.0-rc) independently adds an overlapping client-auth implementation to clients/rust/src/client.rsFyndClientBuilder::with_api_key plus with_quote_path/with_health_path path overrides and a non-retryable FyndError::RateLimited (HTTP 429) variant. Both PRs touch client.rs and will conflict; they should be reconciled into one auth API. Note the two currently disagree on header format: this PR sends Authorization: Bearer <key> while #327 sends the raw key. The raw form is what the deployed gateway expects — verified against the live prod gateway with an admin key (raw key → 200, Bearer <key> → 401), since ph-nginx-auth's auth.lua matches the entire raw Authorization value against user_tokens.token with no Bearer stripping.

@tamaralipows tamaralipows left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this - just one question/request

("arbitrum", 42161),
("bsc", 56),
("polygon", 137),
("unichain", 130),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

plasma? 👀

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it really make sense to add Plasma here since we don't have a hosted Tycho for Plasma? 🤔

tvinagre and others added 2 commits July 20, 2026 19:22
The hosted gateway at fynd-api.propellerheads.xyz requires a bearer
token and per-chain request paths (/v1/{chain}/quote). Neither typed
client could produce those requests, forcing users onto raw curl/fetch.

Adds with_api_key/with_chain to the Rust FyndClientBuilder (HostedConfig
struct) and apiKey/chain/headers to the TypeScript FyndClientOptions.
The TS chain routing is an openapi-fetch middleware that rewrites /v1/
to /v1/{chain}/, keeping call sites typed against the schema literals.
createFyndClient and the Middleware type are now re-exported from the
package root.

Also fixes a signing bug: build_quote_only hardcoded chain_id 1, so a
quote-only client on a non-mainnet chain would sign transactions
stamped for mainnet whenever SigningHints supplied nonce, fees, and
gas. The chain ID is now derived from the chain slug. build() checks
the slug against the chain the RPC node reports.

Both options are opt-in; unset, they reproduce the previous
unauthenticated requests to unprefixed paths.
The deployed ph-nginx-auth gateway matches the entire raw Authorization
header value against user_tokens.token, with no Bearer stripping. Verified
against the live prod gateway with an admin key: a raw key returns 200 while
`Authorization: Bearer <key>` returns 401. Send the key verbatim (Rust and
TypeScript clients) and update the doc comments and header assertions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@tvinagre
tvinagre force-pushed the tl/hosted-api-client-auth branch from e30aa42 to 688eef3 Compare July 20, 2026 22:22
@tvinagre
tvinagre enabled auto-merge July 20, 2026 22:22
@tvinagre
tvinagre force-pushed the tl/hosted-api-client-auth branch from 688eef3 to 4d63aaa Compare July 21, 2026 13:28
@tvinagre
tvinagre merged commit e16457c into main Jul 21, 2026
15 checks passed
@tvinagre
tvinagre deleted the tl/hosted-api-client-auth branch July 21, 2026 13:31
@propellerci

propellerci Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

This PR is included in version 0.95.0 🎉

@propellerci propellerci Bot added the true label Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants