I’m following the steps here to set up a custom domain so my users don’t have to remember (or ever see) the non-user-friendly canister-id.ic0.io domains and can instead use my domain.
I’ve gotten it to work with my frontend canister by following the steps linked page, but I’m having trouble getting it to work with one of my other canisters that implements http_request since it needs to return programmatically generated xml.
When I try to run the curl command shown here, I get an error response stating
failed to retrieve known domains from canister mvjun-2yaaa-aaaah-aac3q-cai. This implies that my assets/.well-known/ic-domains file isn’t accessible, and indeed it isn’t when I try to request the file in a browser, whereas requesting the same file in my frontend canister works just fine.
I assume this is because my frontend canister is declared in dfx.json to be of type “asset” whereas the canister I’m failing to get my custom domain to work on is declared to be of type “motoko” since I use that canister to respond programmatically in the http_request function.
When navigating to the “this file” link above, I get an InternetComputer styled “Page failed to load” error page, not the simple text string containing my domain as I get when navigating to the “the same file” link above.
I’ve tried hard-coding the custom domain inside my http_request function, which works, but only when requesting the raw version of the url, which the curl command specified in the troubleshooting section does not request and presumably also the inner workings of the custom domain logic does not request either since the registration call doesn’t work and gives the “failed to retrieve known domains” error.
How can I get my custom domain working on a motoko canister (not an asset canister) that is set up to respond to raw calls in http_request, not to asset file calls?
The file needs be certified otherwise the custom-domain-logic wouldn’t know for sure if the canister wants to be able to be served at that domain.
Both raw calls and non-raw calls are the served through the http_request function. The difference is that for non-raw calls, the canister must return a certificate in one of the response headers, otherwise the response is blocked. To create the certificates for the files you can use a certification library like ic-certification.
Thank you @levi! That makes sense.
Indeed, the ic-certification library makes it so I can serve a raw file without the .raw in the URL, as shown on this demo page. Thanks @nomeata, and thanks @levi for bringing it to my attention. I’ll see if I can get this working for my assets/.well-known/ic-domains file so the registration call for my custom domain will succeed.
I wonder if I’m the first person who’s tried to get custom domains working for non-frontend canisters, and if not, if the custom domain docs should be updated to include instructions for certifying this file since it’s not certified by default in a canister with the “motoko” type.
It’s good to see that the
ic-certification library is bringing you in the right direction.
In case you’re interested, some other canisters serving custom
http_request endpoints instead of an asset canister on custom domains include Internet Identity, NNS Dapp, Juno, and Taggr. However, they are all implemented in Rust rather than Motoko, but the code may still be helpful.
I agree the documentation is lacking in this department. It’s something that I intend to work on in the coming months. My focus will initially be on the Rust side, but Motoko will come later if the community doesn’t pick up BNT-13 in the meantime.
Ok, using the ic-certification library I was able to get my ic-domains file to be served and certified from my domain (https://cureate.art/.well-known/ic-domains) so I could finish the custom domains tutorial with the registration call succeeding.
Is it possible to have a custom domain responding to all the other http_request calls without having to certify each one (i.e. as though I was calling the raw.ic0 address)? I don’t care that those responses are certified, I just don’t want the URL to look like someone smashed their forehead into the keyboard.
For example, this URL loads the XML I’m returning from http_request:
But this URL does not, and I get back “Body does not pass verification”:
(I have the DNS alias for rss.cureate.art set to icp1.io just like cureate.art is, though the TXT record for _canister-id.cureate.art points to my frontend canister and _canister-id.rss.cureate.art points to my motoko canister which responds to http_request)
I guess I’m looking for a way to set up the DNS so it points to the raw.ic0.app address and gets back the un-certified plain text without having the URL change to that address with the CID in it like a basic forward would. Is that just inherently insecure and I should give up on trying to accomplish this and just deal with users sometimes seeing the ugly URL?
Note that it’s not feasible for me to store the hashes for the resulting XML for every possible URL (like the ic-certification demo page does) since the XML will change based on a number of factors that could change at any time, not just during canister updates and a small number of update calls.
So I guess my question boils down to: is it possible to have a custom domain that returns non-certified responses?
There are a few approaches you could take to do this.
The problem with RAW is it allows individual replicas to choose whether or not your response should be verified. This shouldn’t be the case as individual replicas should be considered untrusted. It should be a canister decision and that decision should go through consensus.
One approach is to set up your own HTTP Gateway that skips verification entirely, although I don’t recommend that at all.
You could consider using motoko-server or motoko-certified-cache. The certified cache package will tell the HTTP Gateway to upgrade to an update call, cache the response, and then on subsequent queries to the same endpoint it will serve the cached response instead of upgrading to an update call. This is probably your best option at the moment.
Alternatively, you could implement this logic yourself and always upgrade to an update call.
Another approach is to leverage response verification v2, where you can skip certification on the canister side. Unfortunately, this doesn’t have support in Motoko yet, and that’s what BNT-13 is about.
A final approach, but not one that’s available yet, is to use a different form of certification that involves the boundary nodes making multiple query responses and aggregating the results. I don’t know when this feature will be available though.