Everything in this toolkit can be done by hand. But if you are comfortable running a command or two, these small Python scripts make it quick and exact — pinning a page as a canonical bafkrei CID, and generating your seven records without a single typo. No private keys, ever.
Python 3 installed (on Windows, tick "Add Python to PATH" during install), and one library. In a terminal:
You will also need a Pinata JWT — see Document 2 for how to create one.
Pins a single HTML file and returns a canonical bafkrei CID. The key line is options = {"cidVersion": 1} — that is what makes the CID bafkrei instead of the bafybei you get from the website. The script also warns you if the result is not bafkrei.
#!/usr/bin/env python3
"""
CPR ENS Toolkit — Pinata upload (single file)
=============================================
Pins ONE HTML file to IPFS and returns a canonical bafkrei... CID.
WHY THIS SCRIPT (and not just the Pinata website):
The website's upload gives you a bafybei... CID. This script forces
cidVersion:1, which gives you a bafkrei... CID — the canonical CPR
format. Both resolve, but bafkrei is the CPR standard.
USAGE:
1. Paste your Pinata JWT below (between the quotes).
2. pip install requests
3. python pinata_upload.py yourpage.html
4. It prints your CID and the exact ipfs://... line to paste into
your ENS name's Content Hash field.
Your JWT is a secret — never share this file with the JWT filled in.
"""
import os, json, sys
# ---- paste your Pinata JWT between the quotes ----
PINATA_JWT = "PASTE_YOUR_JWT_HERE"
# --------------------------------------------------
PIN_ENDPOINT = "https://api.pinata.cloud/pinning/pinFileToIPFS"
def main():
try:
import requests
except ImportError:
sys.exit("Run: pip install requests then try again.")
if PINATA_JWT == "PASTE_YOUR_JWT_HERE" or not PINATA_JWT.strip():
sys.exit("Paste your Pinata JWT into PINATA_JWT first.")
if len(sys.argv) < 2:
sys.exit("Usage: python pinata_upload.py yourpage.html")
path = sys.argv[1]
if not os.path.exists(path):
sys.exit(f"File not found: {path}")
name = os.path.basename(path)
headers = {"Authorization": f"Bearer {PINATA_JWT}"}
with open(path, "rb") as fh:
files = {"file": (name, fh, "text/html")}
meta = {"name": name}
options = {"cidVersion": 1} # <-- forces bafkrei... CIDs
data = {
"pinataMetadata": json.dumps(meta),
"pinataOptions": json.dumps(options),
}
r = requests.post(PIN_ENDPOINT, headers=headers, files=files, data=data, timeout=60)
if r.status_code == 200:
cid = r.json()["IpfsHash"]
print(f"\n Pinned: {name}")
print(f" CID: {cid}")
if not cid.startswith("bafkrei"):
print(" [WARNING] CID is not bafkrei — check that cidVersion:1 is set.")
print(f"\n Paste this into your ENS name's Content Hash field:")
print(f" ipfs://{cid}\n")
else:
sys.exit(f" ERROR {r.status_code}: {r.text[:200]}")
if __name__ == "__main__":
main()
It prints your CID and the exact ipfs://... line to paste into your name's Content Hash field.
Builds the seven CPR record values for your name, ready to paste into the ENS app. It does not touch the blockchain and never asks for a private key — you set and sign the records yourself in the ENS app, which is the safe way.
#!/usr/bin/env python3
"""
CPR ENS Toolkit — Records generator
===================================
Builds the seven canonical CPR records for a name, ready to paste into
the ENS app. It does NOT touch the blockchain and never asks for a
private key — you set and sign the records yourself in the ENS app,
which is the safe way.
USAGE:
python records_helper.py <name> <hyphenated-root> <VERTICAL>
EXAMPLE:
python records_helper.py agenticorchestrator agentic-orchestrator DEV.ORCHESTRATOR
...prints:
cat dev-infrastructure
type agentic-orchestrator-root
purpose canonical-agentic-orchestrator
rights lease-eligible
mapping inheritance:infra
schema ensv2-taxonomy-v1
vertical DEV.ORCHESTRATOR
NOTES:
- <name> : your name without .eth (e.g. agenticorchestrator)
- <hyphenated-root> : the same name with word breaks (e.g. agentic-orchestrator)
- <VERTICAL> : the CPR vertical in dot form, UPPERCASE (e.g. DEV.ORCHESTRATOR)
Get your vertical and the correct hyphenation from the CPR ENS Scorer —
it shows all seven values for any T8+ name already.
"""
import sys
SCHEMA = "ensv2-taxonomy-v1"
MAPPING = "inheritance:infra"
RIGHTS = "lease-eligible"
def build_records(name, hyphenated, vertical):
family = vertical.split(".")[0].lower() # CHAIN.L2.DA -> chain
return {
"cat": f"{family}-infrastructure",
"type": f"{hyphenated}-root",
"purpose": f"canonical-{hyphenated}",
"rights": RIGHTS,
"mapping": MAPPING,
"schema": SCHEMA,
"vertical": vertical.upper(),
}
def main():
if len(sys.argv) != 4:
sys.exit("Usage: python records_helper.py <name> <hyphenated-root> <VERTICAL>")
name, hyphenated, vertical = sys.argv[1], sys.argv[2], sys.argv[3]
recs = build_records(name, hyphenated, vertical)
print(f"\n Seven records for {name}.eth")
print(f" (paste each key/value as a Text record in the ENS app)\n")
for k, v in recs.items():
print(f" {k:9} {v}")
print("\n Then set Content Hash separately to your ipfs://... CID.\n")
if __name__ == "__main__":
main()
It prints all seven records. Get your vertical and the correct word-break from the CPR ENS Scorer — it already shows all seven for any T8+ name.
If you manage a portfolio of names, the download includes a batch version — the same script CPR uses to pin its own redirect files. Put your HTML files in a ./redirects folder named <name>.html, list them in the script, and it pins them all (still cidVersion:1, still checking for bafkrei), writing a cids.json table. It is in the zip as cpr_batch_upload.py.
Put together with the rest of the toolkit: