Running B3Note on mainnet
Deploying
Section titled “Deploying”icp deploy -e production --no-create # both canistersicp deploy frontend -e production --no-create # the frontend onlyThe deploy upgrades the canisters recorded in .icp/data/mappings/production.ids.json: backend
xeka7-ryaaa-aaaal-qb57a-cai and frontend 4lidq-zqaaa-aaaap-abkbq-cai. --no-create makes it stop
rather than create a canister that is not recorded there. Do not deploy with -e ic: that is icp-cli’s
built-in mainnet environment, which records none of these ids, so it creates new canisters. A new
frontend is a new origin, which gives every user a new principal, and the custom domain would still
point at the old one.
The production environment uses the vetKD key key_1, keeps the AI assistant off and sets strict
limits (icp.yaml). The frontend reads the backend’s id from the asset canister’s ic_env cookie on
*.icp0.io, and from frontend/src/lib/origins.ts on a custom domain (Custom domain).
A build with CANISTER_ID_BACKEND=<id> set uses that id everywhere.
Limits, costs and settings
Section titled “Limits, costs and settings”-
vetKD derivations cost about 0.026 T cycles each on
key_1. The app derives one key per Internet Identity user per browser (then caches it), plus one per share view; guests cost nothing. Production allows 5 derivations per user and 20 in total per hour, so the worst case is about 0.5 T an hour. Raise the limits as real use grows. -
Controllers can change limits, models and the AI switch at runtime.
limitsreplaces the whole record:Terminal window icp canister call backend get_config '()' -e productionicp canister call backend update_config '(record { ai_enabled = opt true; llm_model = opt "llama4-scout" })' -e production -
The LLM’s free models need no cycles. Set
llm_cycles_per_callfor paid models.
Custom domain
Section titled “Custom domain”The frontend is also served at https://note.b3pay.net. Internet Identity
gives a user one principal per origin, so a page there signs in with derivationOrigin set to the
canister’s own address, https://4lidq-zqaaa-aaaap-abkbq-cai.icp0.io: a user has the same principal, and
so the same notes, at both addresses. frontend/src/lib/origins.ts holds the production ids and the
custom domains, and the client uses them only on a custom domain:
- Sign-in: Internet Identity accepts the
derivationOriginbecause the canister’s address lists the domain in/.well-known/ii-alternative-origins. Internet Identity’s page fetches that file from another origin, so.ic-assets.json5serves it withAccess-Control-Allow-Origin: *, and keeps the.well-knownfolder, which the asset canister would otherwise skip. - Calls go to
https://icp-api.io, and to the production backend by its id: theic_envcookie is not read there, because any other subdomain ofb3pay.netcould set it. - The HTTP gateways serve the domain only if the canister lists it in
/.well-known/ic-domains.
frontend/tests/customDomain.test.ts checks that origins.ts and both .well-known files name the same
domains. To add one, add it to all three, deploy the frontend, check that
https://4lidq-zqaaa-aaaap-abkbq-cai.icp0.io/.well-known/ii-alternative-origins returns the JSON (not the
app’s HTML), then add these DNS records, as forge.b3pay.net has them (on Cloudflare, DNS only, not
proxied):
| Type | Name | Value |
|---|---|---|
| CNAME | note.b3pay.net |
note.b3pay.net.icp1.io |
| TXT | _canister-id.note.b3pay.net |
4lidq-zqaaa-aaaap-abkbq-cai |
| CNAME | _acme-challenge.note.b3pay.net |
_acme-challenge.note.b3pay.net.icp2.io |
and register the domain with the boundary nodes:
curl -sL "https://icp.net/custom-domains/v1/note.b3pay.net/validate" # "success" once DNS is rightcurl -sL -X POST "https://icp.net/custom-domains/v1/note.b3pay.net" # starts registrationcurl -sL "https://icp.net/custom-domains/v1/note.b3pay.net" # until "registered"A browser keeps two things per origin, so they do not follow a user to the new address: a guest’s key (in localStorage; a guest signs in there by importing their recovery key), and the cached vault key (in IndexedDB; an Internet Identity user’s first unlock there derives it again, which counts toward the per-user vetKD limit).