Idempotent Proxy: proxy HTTPS Outcalls to any Web2 service

I am applying for a $5k Developer Grant for idempotent-proxy.

The idempotent-proxy is a reverse proxy service written in Rust with built-in idempotency support.
When multiple requests with the same idempotency-key arrive within a specific timeframe, only the first request is forwarded to the target service. The response is cached in Redis, and subsequent requests poll Redis to retrieve and return the first request’s response.
This service can be used to proxy HTTPS Outcalls for ICP canisters, enabling integration with any Web2 service. It supports returning only the necessary headers and, for JSON data, allows response filtering based on JSON Mask to return only required fields, thus saving cycles consumption in ICP canisters.

Please review my application and discuss! See DFINITY’s directions for becoming a registered reviewer here. They will be collected by DFINITY. When one week passes, DFINITY will release them and they will appear as a new section on this post.

Please review my application and discuss! If you would like to submit an official review, then please use the links below to register, see the rubric, and submit a review.

I’m looking forward to everyone’s input!

Reviewer Registration | Rubric for Evaluating | Submit a Review



1 Like

Idempotency-Key is a superfluous idea for our use case (outcalls on IC).

Really, idempotency can be determined by hash of the request (headers and body). The hash is always the same for the same outcall. Reversely, if the hash is the same, the returned information by the logic of the outcalls should be the same. So, hash of the outcall body and headers can play the role of Idempotency-Key, rendering Idempotency-Key useless.

Well, suppose, we ask an oracle for a price of crypto. Then two requests should have different answers in the case if they are done at different time. But if we set caching period low, they will have different answers, anyway. So, I think, in this case Idempotency-Key is also of no much usefullness. Moreover, if the client indeed provides a header with a random value, then it will change the hash (without proxy having special support for namely this header), rendering Idempotency-Key totally useless.

Also, it should be X-Idempotency-Key rather than Idempotency-Key accordingly HTTP standard.

If you’re aware of the $LUNA price crash, you would know not to make such assumptions.

I’m not particularly concerned about whether to use X-Idempotency-Key or Idempotency-Key. I try to follow RFC 6648 - Deprecating the "X-" Prefix and Similar Constructs in Application Protocols, and Stripe also uses Idempotency-Key.

Loosely copied from another thread:

If we pass Idempotency-Key in the headers, it changes the hash of the request data, so no need to specify the particular header for this usage. It is however useful to have a configurable list of headers to be removed from the request. If we observe the hash and remove Idempotency-Key, this will have exactly the same effect as handling Idempotency-Key expolicitly.

Here is another similar Developer Grant application initiated by @qwertytrewq, which we have discussed:

The app should use tECDSA pub key retrieved from an IC canister to verify the Bearer token, not a key stored in an environment variable.

Usage of plain ECDSA token is almost as unsafe (against hacked canister hardware) as using a fixed secret.

I am writing my own implementation of a similar proxy with tECDSA support in a competing grant, which will contain reliable (tECDSA) authentication.

Not that there there is a complex algorithm that I discontinued in regard of more robust and simple usage of tECDSA for the canister.

Idempotent Proxy allows canisters to integrate with any Web2 service that provides an HTTP API through HTTPS Outcalls.

For example, similar to the recently launched evm-rpc-canister, we can use Idempotent Proxy to proxy requests to an EVM RPC Provider for EVM integration. This method is equally secure and reliable but more cost-effective and flexible in terms of the APIs it can use. Similarly, Idempotent Proxy can proxy requests to a BTC RPC Provider, making integration with the Bitcoin network simpler.

I don’t like arguing, but I want to point out that qwertytrewq has some misunderstandings about ICP and Idempotent Proxy technical details. I’m glad to see he’s starting to recognize some misconceptions in his thread.

Hi @zensh,

Would you consider adding a light version to this proposal that doesn’t depend on Redis and could be deployed as a cloud worker/function?

That’s a great suggestion. I’ll implement it.

1 Like

Implemented an Idempotent Proxy as a Cloudflare Worker using TypeScript, a first-class language for Cloudflare Workers. If written in Rust, it would run through the WASM virtual machine. Next steps include improving testing and providing canister integration examples.

1 Like

Meanwhile, yesterday I released a beta of my competing product Join Proxy.

My proxy features validation of the call by callback to IC, what warrants that calls can’t be made even by a malevolent replica. Zensh didn’t address this issue what makes my product clearly superior.

I provide full automated testing in Docker.

So, apparently, the only drawback of my product compared to zensh’s one is that I don’t provide Redis support. But I provide pluggable API, what makes adding Redis support easy.

I want to point out that qwertytrewq has some misunderstandings about ICP and Idempotent Proxy technical details.

Concerning Zensh claims about my misunderstandings, I really had some security misunderstanding, but after some thinking provided this callback-based reliable verification.

Where I had misunderstandings, Zensh had no understanding: he didn’t provide any secure way to validate this with blockchain-grade security, while I did.

Oh, I also didn’t provide CloudFlare version, only Rust standalone server.

To be precise, Zensh’s software allows to a malignant replica to steal OpenAI tokens, for example. Mine doesn’t.

Idempotent Proxy hides sensitive information like keys by defining constants such as URL_xxx or HEADER_xxx on the server side. For example:

Sensitive information is included in URL_HTTPBIN and HEADER_TOKEN constants in the proxy server’s .env file, which are used directly when calling APIs from the canister:

curl -v -X GET 'http://localhost:8080/URL_HTTPBIN' \
  -H 'idempotency-key: idempotency_key_001' \
  -H 'authorization: HEADER_TOKEN' \
  -H 'content-type: application/json'

This mechanism not only hides sensitive data but also makes updates easier without needing to modify and redeploy the canister.

Idempotent Proxy uses the proxy-authorization header to verify the requester’s identity. Public keys can be added in the .env file using ECDSA_PUB_KEY_xxx or ED25519_PUB_KEY_xxx constants. The canister generates a proxy_token using ecdsa::sign_with_ecdsa to call the proxy service, which then verifies the proxy_token with the public keys.

curl -v -X GET 'http://localhost:8080/URL_HTTPBIN' \
  -H 'idempotency-key: idempotency_key_001' \
  -H 'proxy-authorization: Bearer 6LduPbIpAAAAANSOUfb-8bxxx...xxxU45eilZFSmlSguN5TO' \
  -H 'authorization: HEADER_TOKEN' \
  -H 'response-headers: content-type,content-length' \
  -H 'x-json-mask: args,url' \
  -H 'content-type: application/json'

Apologies for the sparse documentation of Idempotent Proxy, which makes it difficult for developers to see these details. I will update the documentation as soon as possible.

I am developing ckDOGE, which will communicate with Dogecoin chain nodes through an Idempotent Proxy. Here is the agent definition for ck-doge-canister accessing the proxy:

The relevant Dogecoin JSON-RPC API definitions are here:

.env file can be read by a malignant replica. Your code isn’t secure enough.

I’m not sure what you mean by “malignant replica.” The .env file is used by the proxy service and is a part of Web2 infrastructure. If you’re using Cloudflare Worker, you can store the keys in Cloudflare Worker’s secrets. However, you should be aware that no regular Web2 infrastructure can be absolutely secure, and the confidentiality requirement for an OpenAI API key is not that high.

I understanad this, but a malignant replica can forge a request to the server, causing it to use URL_xxx or HEADER_xxx without the owner of the canister requesting this. So, a malignant replica can steal OpenAI tokens.

In the supposition that the part of Web2 infrastructure that we manage ourselves is without compromises:

  • A hacker that owns a replica (I call it a malignant replica, for short) can cause additional requests to your server (that it may forward to OpenAI and so tokens are stolen), if we use your software.
  • With my software there is no such security vulnerability.

the confidentiality requirement for an OpenAI API key is not that high.

That’s not an argument.