Configuration
appsettings.json, the optional CCIP API key, RPC URLs, and how the node registers itself
appsettings.json
The node reads its configuration from a Chainlink section:
"Chainlink": {
"BaseUrl": "https://api.ccip.chain.link/v2/",
"NetworkRpcUrls": {
"ethereum-mainnet": "https://your-rpc-provider/...",
"arbitrum-mainnet": "https://your-rpc-provider/...",
"base-mainnet": "https://your-rpc-provider/..."
}
}
| Key | Type | Required | Description |
|---|---|---|---|
BaseUrl |
text | No | Base URL for the CCIP API v2. Defaults to the real public https://api.ccip.chain.link/v2/ — a stable production endpoint, so most deployments never need to set this. |
NetworkRpcUrls |
object | Only for router/isChainSupported & router/getFee |
Map of network name → JSON-RPC endpoint URL (e.g. an Alchemy/Infura project URL). Every other operation on this node works with no RPC configuration at all. |
BaseUrl, no
production default RPC URL ships with this node. An RPC endpoint is deployment-specific infrastructure
(often a paid, rate-limited allocation) that must never be hardcoded as a source default. You must supply
your own NetworkRpcUrls entry for any network you call isChainSupported or
getFee against — see Troubleshooting for what happens
when one is missing.
Optional CCIP API Key
The CCIP API v2 works unauthenticated for the vast majority of its surface — every operation this node implements works with no API key at all. An API key is only needed for one gated CCIP API feature (an intents/quote endpoint this node doesn't implement), and it also raises rate-limit headroom on the public endpoints this node does call.
When present, attach the key as this node's credential (an API_KEY-type vault record, the same
shape used across this codebase). The node resolves it once per execution via
ReadCredentialRawPrimaryAsync and passes it down as a plain per-call parameter — there is no
bespoke credential-resolver service class for this node.
Registration
ChainlinkDependency.RegisterDefaults(services) registers everything the node needs:
- The
ChainlinkApiClientOptionsoptions binding (bound lazily to theChainlinkconfiguration section) ChainlinkRateLimitHandler— a TransientDelegatingHandlerthat retries 429/502/503/504 responses, honoringRetry-After- The CCIP API v2 HTTP client, the message builder, the on-chain reader, and the 3 resource services (Message, Router, Lane)
- The executor itself, scoped
- The
ExecutorRegistryentry forchainlink
new ChainlinkDependency().RegisterDefaults(services); line must be added to
Plugins_RegisterAllNodes() in ServiceCollectionExtensionsForAI.cs, plus a
ProjectReference from the hosting project to this executor's csproj. Without both, the node
type won't appear in the workflow designer even though the package is referenced — this is the same pattern
Binance, Ethereum, HashiCorp, and IPFS all use in that same file.
Project Layout
The node ships as three .NET projects (plus a separate Tests project, not part of the shipped package):
| Project | Responsibility |
|---|---|
BizFirst.Integration.Chainlink.Domain |
Pure result records and shared value types. Zero logic, zero I/O. |
BizFirst.Integration.Chainlink.Services |
The CCIP API v2 HTTP client, the EVM2AnyMessage/extraArgs ABI encoder, the minimal independent on-chain reader, and the 3 resource services. |
BizFirst.Ai.ExecutionNodes.Blockchain.ChainLink |
The executor: (resource, operation) routing, config parsing, operation-info DTOs. |
Chainlink (BizFirst.Integration.Chainlink.Domain/.Services), while the
executor project and this guide's repo use ChainLink. Both spellings refer to the exact same
node — this is simply how the three projects were named.