Custom domain for caffeine.ai dapp

Hi, I’m trying to configure custom domain www.plsak.com for my dapp (by Caffeine): https://plsak-m9e.caffeine.xyz/
Backend Canister: tks3f-kaaaa-aaaas-qbq5q-cai

Frontend Canister: tkq3f-kaaaa-aaaas-qbq5q-cai

My DNS records configuration:

$ host -t cname www.plsak.com
www.plsak.com is an alias for tks3f-kaaaa-aaaas-qbq5q-cai.icp0.io.

$ host -t txt _canister-id.www.plsak.com
_canister-id.www.plsak.com descriptive text "tks3f-kaaaa-aaaas-qbq5q-cai"

Caffeine is very confident on the setup so I configured DNS records as advised, looking on below doc there are a bit different ones ( ...icp1.io, _acme-challenge, …icp2.io …) but Caffeine confirmed that it’s testnet/not-needed:

I got advised that the propagation can take up to 48 hours but as that period has passed and I’m still getting just Unknown Domain (https://www.plsak.com/) would like to ask to review my (and caffeine.ai) setup to identify if is there something incorrect or eventually if is there some limitation on ICP.

Relevant communication with caffeine.ai:

Relevant code by caffeine.ai handling the .well-known directory & its content - WellKnownHandler.tsx:

import React, { useEffect, useState } from 'react';

interface WellKnownFile {
  path: string;
  content: string;
  contentType: string;
}

// Well-known files for domain verification - updated for www.plsak.com
const wellKnownFiles: WellKnownFile[] = [
  {
    path: 'ic-domains',
    content: 'www.plsak.com',
    contentType: 'text/plain'
  },
  {
    path: 'ii-alternative-origins',
    content: JSON.stringify({
      "alternativeOrigins": ["https://www.plsak.com"]
    }),
    contentType: 'application/json'
  }
];

// Static method for handling requests programmatically
export const handleWellKnownRequest = (wellKnownPath: string): void => {
  const file = wellKnownFiles.find(f => f.path === wellKnownPath);
  
  if (file) {
    // Create a blob and download it
    const blob = new Blob([file.content], { type: file.contentType });
    const url = URL.createObjectURL(blob);
    
    // For browser requests, we'll serve the content directly
    document.body.innerHTML = `<pre>${file.content}</pre>`;
    document.title = `.well-known/${wellKnownPath}`;
    
    // Set content type in document
    if (document.head) {
      const metaContentType = document.createElement('meta');
      metaContentType.httpEquiv = 'Content-Type';
      metaContentType.content = file.contentType;
      document.head.appendChild(metaContentType);
    }
  }
};

export default function WellKnownHandler() {
  const [fileContent, setFileContent] = useState<string>('');
  const [contentType, setContentType] = useState<string>('text/plain');
  const [notFound, setNotFound] = useState<boolean>(false);

  useEffect(() => {
    const path = window.location.pathname;
    const wellKnownPath = path.substring('/.well-known/'.length);
    
    const file = wellKnownFiles.find(f => f.path === wellKnownPath);
    
    if (file) {
      setFileContent(file.content);
      setContentType(file.contentType);
      setNotFound(false);
      
      // Set appropriate headers if possible
      if (document.head) {
        const metaContentType = document.createElement('meta');
        metaContentType.httpEquiv = 'Content-Type';
        metaContentType.content = file.contentType;
        document.head.appendChild(metaContentType);
      }
    } else {
      setNotFound(true);
    }
  }, []);

  if (notFound) {
    return (
      <div className="min-h-screen bg-slate-900 text-slate-100 flex items-center justify-center">
        <div className="text-center">
          <h1 className="text-2xl font-bold mb-4">404 - Not Found</h1>
          <p className="text-slate-400">The requested .well-known file was not found.</p>
          <div className="mt-8">
            <h2 className="text-lg font-semibold mb-4">Available .well-known files:</h2>
            <ul className="text-left space-y-2">
              {wellKnownFiles.map((file) => (
                <li key={file.path} className="text-blue-400">
                  <a href={`/.well-known/${file.path}`} className="hover:text-blue-300">
                    /.well-known/{file.path}
                  </a>
                </li>
              ))}
            </ul>
          </div>
        </div>
      </div>
    );
  }

  return (
    <div className="min-h-screen bg-slate-900 text-slate-100">
      <div className="container mx-auto px-4 py-8">
        <div className="bg-slate-800 rounded-lg p-6 border border-slate-700">
          <div className="flex items-center justify-between mb-4">
            <h1 className="text-xl font-semibold">
              .well-known/{window.location.pathname.substring('/.well-known/'.length)}
            </h1>
            <span className="text-sm text-slate-400 bg-slate-700 px-2 py-1 rounded">
              {contentType}
            </span>
          </div>
          <pre className="bg-slate-900 p-4 rounded border border-slate-600 overflow-auto text-sm">
            <code>{fileContent}</code>
          </pre>
          <div className="mt-4 text-sm text-slate-400">
            <p>This file is served for domain verification and configuration purposes.</p>
          </div>
        </div>
        
        <div className="mt-8 bg-slate-800 rounded-lg p-6 border border-slate-700">
          <h2 className="text-lg font-semibold mb-4">Domain Setup Instructions</h2>
          <div className="space-y-4 text-sm">
            <div>
              <h3 className="font-medium text-blue-400 mb-2">DNS Configuration for www.plsak.com:</h3>
              <div className="bg-slate-900 p-4 rounded border border-slate-600">
                <p className="mb-2"><strong>CNAME Record:</strong></p>
                <code className="text-green-400">www.plsak.com CNAME tks3f-kaaaa-aaaas-qbq5q-cai.icp0.io</code>
                
                <p className="mt-4 mb-2"><strong>TXT Record (for verification):</strong></p>
                <code className="text-green-400">_canister-id.www.plsak.com TXT tks3f-kaaaa-aaaas-qbq5q-cai</code>
              </div>
            </div>
            
            <div className="text-slate-400">
              <p><strong>Note:</strong> The canister ID tks3f-kaaaa-aaaas-qbq5q-cai is configured for this domain.</p>
              <p className="mt-2">After setting up these DNS records, your domain www.plsak.com will point to this application.</p>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}


Hey @plsak

It seems that caffeine was hallucinating and confidently leading you down the wrong path:

In order to set up a custom domain, you need to follow all the steps from the docs page that you linked:

  1. Setup the DNS records:
    1. CNAME from www.plsak.com to www.plsak.com.icp1.io (this is missing)
    2. TXT for _canister-id.www.plsak.com with your canister ID (this is correctly set up)
    3. CNAME from _acme-challenge.www.plsak.com to _acme-challenge.www.plsak.com.icp2.io (this is missing)
  2. Setup the .well-known/ic-domains file containing your domain name (this is missing)

Once you have done all of that, you can register the domain by calling:

curl -sL -X POST \
    -H 'Content-Type: application/json' \
    https://icp0.io/registrations \
    --data @- <<EOF
    {
      "name": "www.plsak.com"
    }
EOF

This will return a request ID with which you can check the registration status.

To check the .well-known/ic-domains file, you can use the following command:

curl https://tks3f-kaaaa-aaaas-qbq5q-cai.icp0.io/.well-known/ic-domains
1 Like

For the record, I created a Caffeine app that guides you in the process of assigning a custom domain to your application: DNSter.

Curiosity: the app’s custom domain was associated using the app itself.

I was planning to add text prompts that you could copy and paste into your Caffeine chat to make Caffeine set up your app properly, but I still haven’t done it. It should anyway help you with the process.

3 Likes

Thank you! Let’s see now :crossed_fingers:

You never know, but I’m under the impression that .well-known/ic-domains should probably also be pre-rendered or generated by the canister, rather than at runtime with JavaScript as shown in the snippet above.

2 Likes

:sweat_smile: :crossed_fingers: :folded_hands:

This is also very helpful, thank you! :pray:

1 Like

Looks like not so simple for the AI (few attempts made already) :person_shrugging:

Just to update, AI is now confused whether to setup the files in frontend or backend canister - so far DNS was configured for backend but now starts to tell to use frontend one as per below:

Configuring those files as static frontend files (“prerendered”) instead of generating the information on the backend seems like a pragmatic, and common, solution. Therefore I would say you can follow this approach.

That said, based on your screenshot, I’d expect the files to usually live in a folder called static or public, rather than directly under src but that’s just a guess. Maybe try to use the keyword “static” when asking the AI about configuring it on the frontend side?

Note that I’m not sure how to configure this in the caffeine/dfx setup, you know, it just works out of the box in Juno :wink:. Hopefully this helps though!

1 Like

Yeah that’s actually one of things where I was confident that Caffeine will exactly know how to achieve, but hallucinates,.will give it few more shots when available later, maybe will already know, thanks again!

Yes, this is 100% correct. The custom domain service simply fetches the asset and checks if the domain name appears in it. It does not interpret or render anything.

2 Likes

Hi, just to know, considering that Caffeine still gives invalid directions and is not able to create a working setup (although can do much more complex things), is it possible that there are currently some limitations on purpose (or limited functionality) - for Caffeine generated apps (or subnets) ?

Oki, this is clear now :clap: :crossed_fingers: :saluting_face:

2 Likes

you can make it work now @plsak, but it may be practical for most user to wait for this plan and have caffeine handle it for them.

Custom Domain Registration

Custom Domain Update

1 Like