From 07b02af853adf9f0fa2a7a45b20e0efb23eb54da Mon Sep 17 00:00:00 2001 From: ManulParihar Date: Mon, 7 Sep 2026 13:06:26 +0530 Subject: [PATCH 01/42] Use one spelling of the name and one description everywhere The site shipped four spellings of the product name and two different descriptions of what it is. Search and AI systems build an entity record out of repeated strings, so four surface forms split one product into four weakly supported ones, and two descriptions give no clear answer to "what is this". Add lib/site-copy.ts as the single source for the name and description, and point config and metadata at it. Settle on Kokio: it matches the domain, and the apostrophe form gets rewritten to a curly quote when MDX renders it, so the source and the page never agreed anyway. Also fixes the home page, which had no canonical URL and described itself in three words, and adds a title template so pages stop hardcoding the suffix. Blog posts, legal documents and the roadmap copy still use the old spellings. Those are content changes and are handled separately. --- app/(legal)/privacy-policy/page.tsx | 2 +- app/(legal)/terms-of-service/page.tsx | 2 +- app/blog/[slug]/opengraph-image.tsx | 6 ++-- app/blog/[slug]/page.tsx | 2 +- app/blog/opengraph-image.tsx | 6 ++-- app/blog/page.tsx | 4 +-- app/layout.tsx | 25 ++++++++++++-- app/manifesto/opengraph-image.tsx | 2 +- app/manifesto/page.tsx | 2 +- app/moonpay-return/page.tsx | 2 +- components/blog/cta-content.ts | 6 ++-- components/faqs.tsx | 2 +- components/global/announcement-banner.tsx | 2 +- components/global/footer.tsx | 2 +- components/manifesto/signature-block.tsx | 2 +- components/road-map.tsx | 4 +-- config/site.tsx | 6 ++-- content/manifesto.mdx | 22 ++++++------- lib/site-copy.ts | 40 +++++++++++++++++++++++ 19 files changed, 99 insertions(+), 40 deletions(-) create mode 100644 lib/site-copy.ts diff --git a/app/(legal)/privacy-policy/page.tsx b/app/(legal)/privacy-policy/page.tsx index 9e4fe0e..e691679 100644 --- a/app/(legal)/privacy-policy/page.tsx +++ b/app/(legal)/privacy-policy/page.tsx @@ -6,7 +6,7 @@ import { LegalDocumentView } from "@/components/legal/legal-document"; export const metadata: Metadata = { title: "Privacy Policy", description: - "How Koki'o handles information — a privacy-first, data-minimising eSIM app that collects as little about you as possible.", + "How Kokio handles information. A privacy-first, data-minimising eSIM app that collects as little about you as possible.", alternates: { canonical: "/privacy-policy" }, }; diff --git a/app/(legal)/terms-of-service/page.tsx b/app/(legal)/terms-of-service/page.tsx index c0fee7c..1095099 100644 --- a/app/(legal)/terms-of-service/page.tsx +++ b/app/(legal)/terms-of-service/page.tsx @@ -5,7 +5,7 @@ import { LegalDocumentView } from "@/components/legal/legal-document"; export const metadata: Metadata = { title: "Terms of Service", - description: "The terms governing your use of the Koki'o app and services.", + description: "The terms governing your use of the Kokio app and services.", alternates: { canonical: "/terms-of-service" }, }; diff --git a/app/blog/[slug]/opengraph-image.tsx b/app/blog/[slug]/opengraph-image.tsx index 3c40221..c898493 100644 --- a/app/blog/[slug]/opengraph-image.tsx +++ b/app/blog/[slug]/opengraph-image.tsx @@ -5,7 +5,7 @@ import { renderOgImage, } from "@/lib/og-image"; -export const alt = "Koki'o blog post"; +export const alt = "Kokio blog post"; export const size = OG_IMAGE_SIZE; export const contentType = OG_IMAGE_CONTENT_TYPE; @@ -18,7 +18,7 @@ export default async function Image({ const post = getPost(slug); return renderOgImage({ - eyebrow: post?.tag ?? "Koki'o Blog", - title: post?.title ?? "Koki'o Blog", + eyebrow: post?.tag ?? "Kokio Blog", + title: post?.title ?? "Kokio Blog", }); } diff --git a/app/blog/[slug]/page.tsx b/app/blog/[slug]/page.tsx index 718edb9..aba55da 100644 --- a/app/blog/[slug]/page.tsx +++ b/app/blog/[slug]/page.tsx @@ -37,7 +37,7 @@ export async function generateMetadata({ const url = `${siteConfig.url}/blog/${post.slug}`; return { - title: `${post.title} | Kokio`, + title: post.title, description: post.description, alternates: { canonical: `/blog/${post.slug}` }, openGraph: { diff --git a/app/blog/opengraph-image.tsx b/app/blog/opengraph-image.tsx index 23bd358..b7cafd0 100644 --- a/app/blog/opengraph-image.tsx +++ b/app/blog/opengraph-image.tsx @@ -4,13 +4,13 @@ import { renderOgImage, } from "@/lib/og-image"; -export const alt = "Koki'o Blog"; +export const alt = "Kokio Blog"; export const size = OG_IMAGE_SIZE; export const contentType = OG_IMAGE_CONTENT_TYPE; export default function Image() { return renderOgImage({ - eyebrow: "Koki'o Blog", - title: "Guides, updates, and stories from Koki'o", + eyebrow: "Kokio Blog", + title: "Guides, updates, and stories from Kokio", }); } diff --git a/app/blog/page.tsx b/app/blog/page.tsx index 73f552c..d07908f 100644 --- a/app/blog/page.tsx +++ b/app/blog/page.tsx @@ -34,10 +34,10 @@ export default function BlogIndexPage() { className="h-20 w-auto md:h-24" />

- KOKI'O Blog + Kokio Blog

- Guides, updates, and stories from Koki'o + Guides, updates, and stories from Kokio

Everything about eSIMs, travel connectivity, and what we're diff --git a/app/layout.tsx b/app/layout.tsx index c9f8119..b3a183a 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -3,7 +3,8 @@ import type { Metadata } from "next"; import { Anybody, Lexend } from "next/font/google"; import { cn } from "@/lib/utils"; -import { siteConfig } from "@/config/site"; +import { siteConfig, TWITTER_HANDLE } from "@/config/site"; +import { META_DESCRIPTION, PRODUCT_NAME } from "@/lib/site-copy"; import { NavBar } from "@/components/global/nav-bar"; import { Footer } from "@/components/global/footer"; import { Toaster } from "@/components/ui/toaster"; @@ -24,8 +25,26 @@ const lexend = Lexend({ export const metadata: Metadata = { metadataBase: new URL(siteConfig.url), - title: "Kokio", - description: "Blockchain powered eSIM", + title: { + default: PRODUCT_NAME, + template: `%s | ${PRODUCT_NAME}`, + }, + description: META_DESCRIPTION, + applicationName: PRODUCT_NAME, + alternates: { canonical: "/" }, + openGraph: { + type: "website", + url: siteConfig.url, + siteName: PRODUCT_NAME, + title: PRODUCT_NAME, + description: META_DESCRIPTION, + }, + twitter: { + card: "summary_large_image", + site: TWITTER_HANDLE, + title: PRODUCT_NAME, + description: META_DESCRIPTION, + }, }; export default function RootLayout({ diff --git a/app/manifesto/opengraph-image.tsx b/app/manifesto/opengraph-image.tsx index 9dd5e8e..18198d8 100644 --- a/app/manifesto/opengraph-image.tsx +++ b/app/manifesto/opengraph-image.tsx @@ -9,7 +9,7 @@ import { OG_IMAGE_SIZE, } from "@/lib/og-image"; -export const alt = "Koki'o Manifesto"; +export const alt = "Kokio Manifesto"; export const size = OG_IMAGE_SIZE; export const contentType = OG_IMAGE_CONTENT_TYPE; diff --git a/app/manifesto/page.tsx b/app/manifesto/page.tsx index ec2c15d..8d7ec6e 100644 --- a/app/manifesto/page.tsx +++ b/app/manifesto/page.tsx @@ -14,7 +14,7 @@ export function generateMetadata(): Metadata { const manifesto = getManifesto(); return { - title: `${manifesto.title} | Kokio`, + title: manifesto.title, description: manifesto.description, alternates: { canonical: "/manifesto" }, openGraph: { diff --git a/app/moonpay-return/page.tsx b/app/moonpay-return/page.tsx index 5b6a6ff..2a91b8a 100644 --- a/app/moonpay-return/page.tsx +++ b/app/moonpay-return/page.tsx @@ -4,7 +4,7 @@ export default function MoonpayReturnPage() {

Your payment was successful. You can close this window and return to - the KOKI'O app. Kindly wait for your order to process. + the Kokio app. Kindly wait for your order to process.

diff --git a/components/blog/cta-content.ts b/components/blog/cta-content.ts index 190bbe4..7f7a5b8 100644 --- a/components/blog/cta-content.ts +++ b/components/blog/cta-content.ts @@ -3,18 +3,18 @@ import { APP_URL, TWITTER_URL } from "@/config/site"; export const CTA_CONTENT = { prelaunch: { heading: "Follow along on X", - body: "Stay updated about Koki'o and learn about digital well-being and wellness :)", + body: "Stay updated about Kokio and learn about digital well-being and wellness :)", ctaLabel: "Follow on X", href: TWITTER_URL, inlineText: "Want to learn more?", inlineLabel: "Follow us on X", }, live: { - heading: "Try Koki'o", + heading: "Try Kokio", body: "Tap. Authenticate. Connect.", ctaLabel: "Get the app", href: APP_URL, inlineText: "Ready to travel smarter?", - inlineLabel: "Try Koki'o", + inlineLabel: "Try Kokio", }, } as const; diff --git a/components/faqs.tsx b/components/faqs.tsx index 3693b2c..949645a 100644 --- a/components/faqs.tsx +++ b/components/faqs.tsx @@ -40,7 +40,7 @@ const faqsData = [ { question: "What is the roadmap for Kokio’s service launch?", answer: - "Get your eSIM, KOKI'O alpha is now live in over 200 countries. Key milestones include the Dev and Ops work for beta features in Q1 2026 followed by a beta launch in Q2 alongside ongoing bug fixes and continuous improvements based on user feedback.", + "Get your eSIM, Kokio alpha is now live in over 200 countries. Key milestones include the Dev and Ops work for beta features in Q1 2026 followed by a beta launch in Q2 alongside ongoing bug fixes and continuous improvements based on user feedback.", }, ]; diff --git a/components/global/announcement-banner.tsx b/components/global/announcement-banner.tsx index 13c9351..604354d 100644 --- a/components/global/announcement-banner.tsx +++ b/components/global/announcement-banner.tsx @@ -7,7 +7,7 @@ const AnnouncementBanner = () => {

- KOKI'O is launching soon!{" "} + Kokio is launching soon!{" "} Follow us on{" "} {

- KOKI'O launches soon —{" "} + Kokio launches soon.{" "}

- — The Koki'o team · Arpit, Manul, Tanmay · 2026 + The Kokio team · Arpit, Manul, Tanmay · 2026

); diff --git a/components/road-map.tsx b/components/road-map.tsx index 1379903..dd9e236 100644 --- a/components/road-map.tsx +++ b/components/road-map.tsx @@ -12,14 +12,14 @@ const roadMapData = [ title: "Q1'26", milestones: [ "Working on Beta release", - "With KOKI'O alpha live in over 200 countries, work on features scheduled in beta release with continuous improvement from user feedback.", + "With Kokio alpha live in over 200 countries, work on features scheduled in beta release with continuous improvement from user feedback.", ], }, { title: "Q2'26", milestones: [ "Beta Testing", - "Launch the public beta for testing and prepare to ship KOKI'O to production.", + "Launch the public beta for testing and prepare to ship Kokio to production.", ], }, ]; diff --git a/config/site.tsx b/config/site.tsx index 7cda76e..d542939 100644 --- a/config/site.tsx +++ b/config/site.tsx @@ -3,6 +3,7 @@ import Blog from "@/assets/icons/blog-fill.svg"; import Twitter from "@/assets/icons/twitter-fill.svg"; import GitHub from "@/assets/icons/github-fill.svg"; import Docs from "@/assets/icons/docs-fill.svg"; +import { CANONICAL_DESCRIPTION, PRODUCT_NAME } from "@/lib/site-copy"; export const BLOG_TAGS = [ "eSIM Basics", @@ -27,11 +28,10 @@ export const TWITTER_HANDLE = "@kokiodotapp"; export const APP_URL = "https://kokio.app"; export const siteConfig = { - name: "Kokio", + name: PRODUCT_NAME, url: "https://kokio.app", header: "Experience the Future of Global Connectivity", - description: - "Travel with confidence with Kokio’s travel data plans. Unlike traditional eSIM providers, Kokio leverages cutting-edge blockchain technology for enhanced security, privacy, and ease of use. Enjoy seamless connectivity across over 200 destinations worldwide.", + description: CANONICAL_DESCRIPTION, socials: [ { icon: Twitter, diff --git a/content/manifesto.mdx b/content/manifesto.mdx index 448fd6d..11e6476 100644 --- a/content/manifesto.mdx +++ b/content/manifesto.mdx @@ -1,6 +1,6 @@ --- title: "Manifesto" -description: "Connectivity is a human right. Privacy is its guardian. At Koki'o, privacy isn't a feature or a compliance checkbox. It's the architecture." +description: "Connectivity is a human right. Privacy is its guardian. At Kokio, privacy isn't a feature or a compliance checkbox. It's the architecture." ogHeadline: "Connectivity is a human right. Privacy is its guardian." tldr: - "Connectivity is a human right. Privacy is its guardian." @@ -11,18 +11,18 @@ tldr: Whatever it takes to provide connectivity! -Connectivity is a human right. Privacy is its guardian. At Koki'o, privacy isn't a feature or a compliance checkbox. It's the architecture. +Connectivity is a human right. Privacy is its guardian. At Kokio, privacy isn't a feature or a compliance checkbox. It's the architecture. We shift power from providers to consumers by moving trust out of providers and operators into open code and your own device, which you can verify. ## About For anyone who needs connectivity in a foreign land, -Koki'o is a consumer app where every account is a smart wallet. With a curated eSIM catalogue, it sets users free to cross borders without worrying about roadblocks. +Kokio is a consumer app where every account is a smart wallet. With a curated eSIM catalogue, it sets users free to cross borders without worrying about roadblocks. -If Koki'o wins, people travel freely around the globe without connectivity friction or fear of breaches. +If Kokio wins, people travel freely around the globe without connectivity friction or fear of breaches. -With the ethos of open source, verifiable source of truth and consumer data security practices, Koki'o provides a friendly & practical eSIM store for the masses with privacy as the default — ***never traded for profit***. +With the ethos of open source, verifiable source of truth and consumer data security practices, Kokio provides a friendly & practical eSIM store for the masses with privacy as the default — ***never traded for profit***. > Telco consumer data remains at risk even with the freedom of connectivity that eSIMs were meant to provide. > @@ -44,23 +44,23 @@ The current telecom system operates on a simple assumption: to connect, you must The identity collection is bolted on, by regulation, by habit, and by business models that profit from knowing you. -Koki'o encodes the right to privacy into a smart wallet bound to your device's biometrics *via secure enclave.* Thus, verifiable ownership — no passwords, no middlemen, no data brokers. +Kokio encodes the right to privacy into a smart wallet bound to your device's biometrics *via secure enclave.* Thus, verifiable ownership — no passwords, no middlemen, no data brokers. ## From Consumers to Citizens -Every Koki'o account is a smart contract you control — abstracted, secure, and portable. For fiat users, your full connectivity history stays intact and under your control as you transition to crypto when ready. +Every Kokio account is a smart contract you control — abstracted, secure, and portable. For fiat users, your full connectivity history stays intact and under your control as you transition to crypto when ready. -Koki'o doesn't manage your eSIM. It lets you own it. +Kokio doesn't manage your eSIM. It lets you own it. ## Infrastructure, Not Interference -Telecom today is a gate. Koki'o is a bridge. +Telecom today is a gate. Kokio is a bridge. From wallet to eSIM to data purchase, every interaction lives on a decentralised, distributed ledger — and under your control. We are removing unnecessary intermediaries, eventually onboarding eSIM providers directly on-chain to enable trustless, peer-to-peer provisioning. Open source smart contract suite, auditable, and verifiable by anyone. ## No Backdoors. No Middlemen. -We are building toward a world where you buy bandwidth the way you buy stablecoins — peer-to-peer, permissionless, and auditable. Koki'o is laying that groundwork today. +We are building toward a world where you buy bandwidth the way you buy stablecoins — peer-to-peer, permissionless, and auditable. Kokio is laying that groundwork today. When you protect your data and reject surveillance capitalism, you strengthen the network for everyone. Your autonomy reinforces ours. Privacy, here, is a commons. @@ -70,7 +70,7 @@ The obvious response to all of the above is: *why should we trust you instead?* So we did not build a system that asks for your trust. We built one that does not require it. -Koki'o collects nothing. There is no name, no email, no KYC, nothing that accumulates a profile of you. Not "minimal data," but no signup at all. +Kokio collects nothing. There is no name, no email, no KYC, nothing that accumulates a profile of you. Not "minimal data," but no signup at all. Your account is a smart contract wallet bound to your device's biometrics; your key never leaves the secure hardware of your own phone. There is no password to phish and no credential to leak, because none exists. diff --git a/lib/site-copy.ts b/lib/site-copy.ts new file mode 100644 index 0000000..11549ef --- /dev/null +++ b/lib/site-copy.ts @@ -0,0 +1,40 @@ +/** + * Canonical brand strings. Import these instead of writing the name or the + * description as a literal, so every surface says the same thing byte for byte. + * + * Models build an entity record out of repeated strings. Four spellings of the + * name split one product into four weakly-supported entities, and two different + * descriptions make a model hedge instead of answering. + */ + +/** The only spelling allowed in prose, headings, metadata and JSON-LD `name`. */ +export const PRODUCT_NAME = "Kokio"; + +/** + * Other spellings that have shipped or that users type. Goes in `alternateName` + * so a model can resolve all of them to one entity. Never use these in prose. + */ +export const PRODUCT_NAME_VARIANTS = [ + "Koki'o", + "KOKI'O", + "KOKIO", + "kokio", + "Kokio eSIM", + "Kokio App", +] as const; + +/** Registered company name. Stays uppercase because that is the filed string. */ +export const LEGAL_NAME = "KOKIO SG PTE. LTD."; + +/** + * Full description. Use for JSON-LD, llms.txt and anywhere without a length cap. + * + * Deliberately carries no release-stage or availability claim. Those change, and + * this string is copied into six surfaces at once. + */ +export const CANONICAL_DESCRIPTION = + "Kokio is a privacy-first travel eSIM app. Buy eSIM data plans in over 200 destinations with card, Apple Pay, Google Pay or stablecoins. No KYC and no personal information collected. Every Kokio account is a passkey-controlled smart wallet, so the eSIM is owned by the user rather than held in a provider database."; + +/** Same claims, trimmed to fit a `` without truncation. */ +export const META_DESCRIPTION = + "Kokio is a privacy-first travel eSIM app. Data plans in over 200 destinations, paid by card or stablecoins. No KYC, no personal information collected."; From 42e16c2705f94b3e9ad5f00b50a8a11f6f386157 Mon Sep 17 00:00:00 2001 From: ManulParihar Date: Mon, 7 Sep 2026 13:29:18 +0530 Subject: [PATCH 02/42] Describe the company and site as structured data Crawlers had to infer who publishes this site from prose. Emit an Organization and a WebSite node once from the root layout, so every page carries the same typed facts: legal name, UEN, registered address, contact, and the social and source links that let a reader corroborate them elsewhere. Both nodes list every spelling of the name in alternateName. That is the only machine-readable place saying the four forms are one company rather than four. Facts come from the existing legal config rather than new literals, so the schema and the legal pages cannot drift apart. --- app/layout.tsx | 3 ++ components/json-ld.tsx | 14 ++++++++ lib/schema.ts | 72 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 components/json-ld.tsx create mode 100644 lib/schema.ts diff --git a/app/layout.tsx b/app/layout.tsx index b3a183a..c5b30a9 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -5,6 +5,8 @@ import { Anybody, Lexend } from "next/font/google"; import { cn } from "@/lib/utils"; import { siteConfig, TWITTER_HANDLE } from "@/config/site"; import { META_DESCRIPTION, PRODUCT_NAME } from "@/lib/site-copy"; +import { siteGraph } from "@/lib/schema"; +import { JsonLd } from "@/components/json-ld"; import { NavBar } from "@/components/global/nav-bar"; import { Footer } from "@/components/global/footer"; import { Toaster } from "@/components/ui/toaster"; @@ -63,6 +65,7 @@ export default function RootLayout({ "flex min-h-screen flex-col bg-beach-sky font-sans antialiased" )} > +
{children}
diff --git a/components/json-ld.tsx b/components/json-ld.tsx new file mode 100644 index 0000000..139301f --- /dev/null +++ b/components/json-ld.tsx @@ -0,0 +1,14 @@ +/** + * Renders a schema.org graph as a JSON-LD script tag. + * + * The content is ours, not user input, so serialising it straight into the tag + * is safe. Keep it that way: never pass anything a visitor can influence. + */ +export function JsonLd({ data }: { data: object }) { + return ( + - - - - - - - - - - - - - - - eSIMs Explained Like You're 5: The Complete Guide for Travellers - (2026) | Kokio - - - - - - - - - - - - - - - - - - - - -
- -
-
- -
- eSIM Basics -

- eSIMs Explained Like You're 5: The Complete Guide for - Travellers (2026) -

-

- What is an eSIM, how does it work, and how do you set one up before - your next trip? A plain-English, step-by-step guide - no jargon, no - assumptions. -

-
-
-
- Arpit Kumar -
-
-
- Arpit Kumar -
- Co-founder -
-
- -
- 6 min read -
-
-
- -
-
-

- TL;DR -

-
    -
  • - An eSIM is a digital SIM - same networks, same signal, no - plastic. -
  • -
  • - Setup takes about 3 minutes at home, before you fly. -
  • -
  • - A week of travel data typically costs $5–9 vs $10–15 per day - for roaming. -
  • -
  • - Your home number stays active for OTPs and WhatsApp the whole - time. -
  • -
-
-
-

- You're at the airport. Your flight lands in six hours in a - country where your phone plan doesn't work. You have three - options: pay your carrier roaming charges that could buy a nice - dinner every single day, hunt down a SIM card kiosk after landing - and hand over your passport to a stranger, or do something that - takes three minutes from your couch right now. -

-

- That third option is an eSIM. And if you've heard the word - but never quite understood it, this guide is for you. By the end, - you'll know exactly what an eSIM is, how it works, and how to - set one up yourself - no tech background required. -

-

- First: what does a SIM card actually do? -

-

- That little plastic chip in your phone does one job: it proves to - a mobile network who you are, so the network agrees to give you - calls, texts, and data. Think of it as a membership card. The - network sees the card, recognises the membership, and lets you in. -

-

- That's it. That's the whole job. Which raises an obvious - question: why does that need to be a physical piece of plastic? -

-

- An eSIM is the same thing, minus the plastic -

-

- eSIM stands for "embedded SIM." Instead of a removable - chip, your phone has a tiny built-in chip that can - download the membership card digitally. Same job, same - networks, same signal - the only difference is how it gets into - your phone. A physical SIM arrives by mail or across a shop - counter. An eSIM arrives by QR code, usually in under three - minutes. -

-

- Nothing about the actual connection changes. Your phone talks to - the same cell towers with the same quality. If a physical SIM is a - paper ticket, an eSIM is the ticket in your phone's wallet - app. The train doesn't care which one you carry. -

-

- Why travellers specifically should care -

-

Three reasons, in order of pain:

-

- 1. Money. International roaming from your home - carrier commonly runs $10–15 per day for a usable data pass - and - pay-per-use rates without a pass can hit several dollars - per megabyte. A local eSIM for the same country is - frequently $5–9 for an entire week. Over a two-week trip, - that's the difference between pocket change and a $150+ line - item on your bill. -

-

- 2. Time. With a physical SIM abroad, you land - tired, find a kiosk, queue, hand over your passport, wait for - someone to photocopy it, and fiddle with a paperclip. With an - eSIM, you install it at home before you fly and turn it on when - the wheels touch down. You have Google Maps before you clear - immigration. -

-

- 3. Your own number stays alive. This is the part - almost nobody tells you: modern phones run two SIMs at once. Your - home SIM stays in the phone receiving OTPs, bank alerts, and - WhatsApp messages, while the eSIM handles data. You don't go - dark at home just because you're abroad. -

-

- Does your phone support eSIM? -

-

- Almost certainly yes, if it was made in the last five or six - years. iPhone XS/XR (2018) and newer, Google Pixel 3 and newer, - most Samsung Galaxy S20+ era flagships and newer. Ten-second - check: -

-
    -
  • - Dial - *#06# - to see a list of Device informations, having - EID in the list means your device is eSIM - compatible. -
  • -
  • - iPhone: Settings → General → About → scroll - down. If you see "Available SIM" or "Digital - SIM," you're set. -
  • -
  • - Android: Settings → search "eSIM" or - "SIM manager." -
  • -
-

- One caveat: phones bought on carrier instalment plans are - sometimes carrier-locked. A locked phone can't use another - network's eSIM until it's unlocked - usually a free - request once the phone is paid off. -

-

- How to set up your first eSIM, step by step -

-
    -
  1. - Buy a plan for your destination from an eSIM - provider's app or website. You'll pick a country (or - region), an amount of data, and a duration. -
  2. -
  3. - Receive a QR code - instantly, by email or in - the app. -
  4. -
  5. - Install it: Settings → Mobile/Cellular → Add - eSIM → scan the QR code. Your phone downloads the profile in - about a minute. Do this at home on WiFi, before you travel. -
  6. -
  7. - Label it something like "Japan trip" - so your SIM list stays sane. -
  8. -
  9. - Configure two switches: set your - data to the travel eSIM, and leave your home SIM active - for calls/texts if you want to stay reachable. Turn OFF data - roaming on your home SIM specifically - that's - what protects you from surprise charges. -
  10. -
  11. - On landing: toggle the travel eSIM on (if you - hadn't already). Within a minute or two you'll see - bars. That's it. -
  12. -
-

- The first time takes five minutes because you're reading - instructions. Every time after that, it takes ninety seconds. -

-

- The seven mistakes first-timers make -

-
    -
  1. - Installing the eSIM on hotel WiFi instead of at home. - Install before you fly; activation abroad then takes seconds. -
  2. -
  3. - Deleting the eSIM instead of turning it off. - Most eSIM QR codes are single-use. Deleting the profile mid-trip - can mean buying a new plan. Toggle off, don't delete. -
  4. -
  5. - Leaving data roaming ON for the home SIM. This - is how people use an eSIM and still get a roaming bill. - Home SIM: roaming off. Travel eSIM: roaming on (for the travel - eSIM, "roaming" is normal - it's how regional - plans work). -
  6. -
  7. - Buying single-country plans for multi-country trips. - Regional plans exist - one eSIM can cover a whole continent or a - custom set of countries. -
  8. -
  9. - Assuming the eSIM gives you a phone number. - Most travel eSIMs are data-only. Calls happen over - WhatsApp/FaceTime - which is how you were going to call anyway. -
  10. -
  11. - Not checking the phone is unlocked. Sixty - seconds of checking beats discovering it at 30,000 feet. A - locked phone restricts network access to a single original - carrier. -
  12. -
  13. - Waiting until the trip to learn all this. You - just fixed that one. -
  14. -
-

- The honest downsides -

-

- eSIMs are not magic. Data-only plans mean no local number (a - problem only if you need to receive local SMS, e.g., some local - delivery apps). Very old or budget phones may lack support. And if - you swap phones mid-trip, moving an eSIM is more fiddly than - moving a plastic chip. For 95% of travellers, none of these - outweigh the savings and convenience. -

-

- What's next -

-

- That's the mechanics - the how. In next week's - post, we'll talk about something almost nobody discusses: - what happens to your passport photo and personal details after you - hand them over for a SIM card, and where that data actually ends - up. If you've ever bought a SIM abroad, you'll want to - read it. -

-
-
-
- -
-

Get early access

-

- Join the Telegram group for early access -

-
-
- Join the Telegram group -
-
-
- -
- -
-
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/next.svg b/public/next.svg deleted file mode 100644 index 5174b28..0000000 --- a/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg deleted file mode 100644 index d2f8422..0000000 --- a/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From 0d09e702f93a8041d126dc00f0d67589cd965375 Mon Sep 17 00:00:00 2001 From: ManulParihar Date: Mon, 7 Sep 2026 15:13:08 +0530 Subject: [PATCH 27/42] Let eslint know the build scripts run in node eslint reported console, process and URL as undefined in scripts/, since the config only described browser and Next code. --- eslint.config.mjs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/eslint.config.mjs b/eslint.config.mjs index b54c97f..6aaf87c 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -21,6 +21,15 @@ export default tseslint.config( }, }, + // Build scripts run in node. Listed by hand rather than pulling in the + // globals package for the few names these scripts actually use. + { + files: ["scripts/**/*.mjs"], + languageOptions: { + globals: { console: "readonly", process: "readonly", URL: "readonly" }, + }, + }, + { ignores: [".next/", "node_modules/", "eslint.config.mjs"], } From 5eb56f59eb02dba60c7c0f08fe2c2538814a33ce Mon Sep 17 00:00:00 2001 From: ManulParihar Date: Mon, 7 Sep 2026 15:13:08 +0530 Subject: [PATCH 28/42] Check definition anchors the same way as answer anchors Both are deep links: a citation points at the one item it used, so the id has to match an element. The check now covers anything a schema set holds. --- scripts/verify-site.mjs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/scripts/verify-site.mjs b/scripts/verify-site.mjs index 9d6e44d..e832e3b 100644 --- a/scripts/verify-site.mjs +++ b/scripts/verify-site.mjs @@ -160,17 +160,15 @@ for (const pathname of sitemapPaths) { fail("schema", `${pathname} references @id ${id}, which nothing defines and no element matches.`); } - // A question's @id is a deep link to the one answer that was cited, so unlike - // the entity ids around it, it has to name something on the page. - for (const node of nodes.filter((entry) => entry["@type"] === "FAQPage")) { - for (const question of node.mainEntity ?? []) { - const fragment = String(question["@id"] ?? "").split("#")[1]; - if (!fragment || !domIds.has(fragment)) { - fail( - "schema", - `${pathname} answers "${question.name}" at ${question["@id"]}, but no element has that id.` - ); - } + // An answer or a definition has an @id so a citation can point at that one + // item, so unlike the entity ids around it, it has to name something on the + // page. Everything a set holds is checked, not the set itself. + const items = nodes.flatMap((node) => node.mainEntity ?? node.hasDefinedTerm ?? []); + + for (const item of items) { + const fragment = String(item["@id"] ?? "").split("#")[1]; + if (!fragment || !domIds.has(fragment)) { + fail("schema", `${pathname} puts "${item.name}" at ${item["@id"]}, but no element has that id.`); } } } From 325114f6be04b6e1dff45051b07519ebc739c367 Mon Sep 17 00:00:00 2001 From: ManulParihar Date: Mon, 7 Sep 2026 15:13:15 +0530 Subject: [PATCH 29/42] Add a glossary Thirteen definitions of the terms the site uses without explaining them, plus a Kokio entry that names every spelling of the product. Definition pages are precise retrieval targets, and each entry has its own anchor so a citation can point at one definition. Reachable from the footer, llms.txt, the sitemap, the full text file and /glossary.md, all reading the same content file. --- app/glossary/page.tsx | 66 ++++++++++++++++++++ app/llms.txt/route.ts | 1 + app/sitemap.ts | 7 +++ components/global/footer.tsx | 7 ++- content/glossary.ts | 114 +++++++++++++++++++++++++++++++++++ lib/schema.ts | 30 +++++++++ lib/site-text.ts | 16 +++++ 7 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 app/glossary/page.tsx create mode 100644 content/glossary.ts diff --git a/app/glossary/page.tsx b/app/glossary/page.tsx new file mode 100644 index 0000000..9569682 --- /dev/null +++ b/app/glossary/page.tsx @@ -0,0 +1,66 @@ +import type { Metadata } from "next"; + +import { glossaryData } from "@/content/glossary"; +import { siteConfig } from "@/config/site"; +import { glossaryGraph } from "@/lib/schema"; +import { PRODUCT_NAME } from "@/lib/site-copy"; +import { JsonLd } from "@/components/json-ld"; + +const DESCRIPTION = `Plain definitions of the terms ${PRODUCT_NAME} uses: eSIM, eUICC, SM-DP+, LPA, passkey, smart wallet, no-KYC and more.`; + +export const metadata: Metadata = { + title: "Glossary", + description: DESCRIPTION, + alternates: { + canonical: "/glossary", + types: { "text/markdown": "/glossary.md" }, + }, + openGraph: { + type: "article", + url: `${siteConfig.url}/glossary`, + siteName: PRODUCT_NAME, + title: `${PRODUCT_NAME} glossary`, + description: DESCRIPTION, + }, +}; + +export default function GlossaryPage() { + return ( +
    + + +
    +
    +

    + Glossary +

    +

    {DESCRIPTION}

    +
    + + {/* Each id is the target of the matching DefinedTerm @id, so a citation + can link to the one definition it used. */} +
    + {glossaryData.map((entry) => ( +
    +
    + {entry.term} + {entry.aliases ? ( + + also {entry.aliases.join(", ")} + + ) : null} +
    +
    + {entry.definition} +
    +
    + ))} +
    +
    +
    + ); +} diff --git a/app/llms.txt/route.ts b/app/llms.txt/route.ts index 0877ebd..c798ce8 100644 --- a/app/llms.txt/route.ts +++ b/app/llms.txt/route.ts @@ -40,6 +40,7 @@ ${PRODUCT_NAME} is also written ${PRODUCT_NAME_SPELLINGS.join(", ")}. Every one ## Product ${link("Home", "/", "what Kokio is, how setup works, features, and the FAQ")} ${link("Manifesto", "/manifesto", "why Kokio treats connectivity as a right and privacy as its guardian")} +${link("Glossary", "/glossary", "eSIM, eUICC, SM-DP+, LPA, passkey, smart wallet and no-KYC defined")} ## Guides ${posts.map((post) => link(post.title, `/blog/${post.slug}`, post.description)).join("\n")} diff --git a/app/sitemap.ts b/app/sitemap.ts index 6ae9468..1a157ac 100644 --- a/app/sitemap.ts +++ b/app/sitemap.ts @@ -2,6 +2,7 @@ import type { MetadataRoute } from "next"; import { siteConfig } from "@/config/site"; import { legalConfig } from "@/config/legal"; +import { GLOSSARY_UPDATED } from "@/content/glossary"; import { getAllPosts } from "@/lib/blog"; import { getManifesto } from "@/lib/manifesto"; import { HOME_UPDATED } from "@/lib/site-copy"; @@ -45,6 +46,12 @@ export default function sitemap(): MetadataRoute.Sitemap { changeFrequency: "yearly", priority: 0.7, }, + { + url: new URL("/glossary", siteConfig.url).href, + lastModified: GLOSSARY_UPDATED, + changeFrequency: "yearly", + priority: 0.7, + }, ...posts.map((post) => ({ url: new URL(`/blog/${post.slug}`, siteConfig.url).href, lastModified: post.updated, diff --git a/components/global/footer.tsx b/components/global/footer.tsx index b6be9db..36df0a0 100644 --- a/components/global/footer.tsx +++ b/components/global/footer.tsx @@ -35,8 +35,9 @@ export const socialLinks = [ }, ]; -const legalLinks = [ +const siteLinks = [ { title: "Manifesto", href: "/manifesto" }, + { title: "Glossary", href: "/glossary" }, { title: "Privacy Policy", href: "/privacy-policy" }, { title: "Terms of Service", href: "/terms-of-service" }, ]; @@ -87,10 +88,10 @@ const Footer = () => {