Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
6f4c96e
Refactor(rdap): share IP arithmetic through common/ip-math.js
jason5ng32 Sep 3, 2026
e6670b1
Feat(ipcalculator): add the pure classification and analysis layer
jason5ng32 Sep 3, 2026
e30fcb1
Feat(ipcalculator): add the IP Calculator advanced tool
jason5ng32 Sep 3, 2026
3b19e02
Docs: describe the IP Calculator and drop changelog entry counts
jason5ng32 Sep 3, 2026
e7074cc
Chore(release): open v7.6.0 with the IP Calculator changelog entry
jason5ng32 Sep 3, 2026
eed1f98
Fix(ipcalculator): use the number-input emoji for the tool card
jason5ng32 Sep 3, 2026
65145ca
Improvements
jason5ng32 Sep 3, 2026
a63ef45
Improvements
jason5ng32 Sep 3, 2026
3cf534d
Improvements
jason5ng32 Sep 3, 2026
d5ed6c9
Feat(connectivity): add Sports import list
Mora140 Sep 4, 2026
e1754e3
Feat(connectivity): add Travel & Booking import list
Mora140 Sep 4, 2026
11536c9
Feat(connectivity): add Perplexity to the AI list
Mora140 Sep 4, 2026
4816612
Fix capitalization of Tripadvisor entry
Mora140 Sep 4, 2026
2a9f623
Improvements
jason5ng32 Sep 4, 2026
238ded1
Merge pull request #478 from Mora140/feat/connectivity-perplexity
jason5ng32 Sep 4, 2026
05f837d
Improvements
jason5ng32 Sep 4, 2026
3da7307
Improvements
jason5ng32 Sep 4, 2026
a16cdf7
Improvements
jason5ng32 Sep 4, 2026
6cf3b58
Fix(speedtest): improve feedback on slow connections
jason5ng32 Sep 5, 2026
e6949bd
Merge pull request #475 from Mora140/feat/connectivity-sports
jason5ng32 Sep 5, 2026
c596a15
Improvements
jason5ng32 Sep 5, 2026
f7b0fb1
Feat(connectivity): add Nigeria import list (#471)
Antisocialworld Sep 5, 2026
a62a017
Merge branch 'dev' of https://github.com/jason5ng32/MyIP into dev
jason5ng32 Sep 5, 2026
56de3a3
Merge upstream/dev into feat/connectivity-travel
Mora140 Sep 5, 2026
0b3b4cc
Feat(ui): add a "Testing…" status to the card-grid tests
jason5ng32 Sep 6, 2026
ddd0a2b
Merge pull request #476 from Mora140/feat/connectivity-travel
jason5ng32 Sep 6, 2026
057e872
Improvements
jason5ng32 Sep 6, 2026
c49cc42
Improvements
jason5ng32 Sep 6, 2026
020bfce
InfoBanner support open sheet
jason5ng32 Sep 8, 2026
a1ee760
Improvements
jason5ng32 Sep 13, 2026
9d9de4e
Refactor(dns): use the native Promise resolver API
jason5ng32 Sep 16, 2026
cc0053a
Refactor(ipinfo): share Intl country names with the frontend
jason5ng32 Sep 16, 2026
a115390
Fix(dnsleaks): obtain bash.ws test IDs from the provider
jason5ng32 Sep 16, 2026
a3b334d
Improvements
jason5ng32 Sep 16, 2026
beb5653
Feat(dns): add SkyDNS (RU) to the resolver list
avirajkmar848-png Sep 16, 2026
7c5c3e8
Fix(ip): reject leading zeros in IPv4 octets
jason5ng32 Sep 17, 2026
3edaaf5
Improvements
jason5ng32 Sep 17, 2026
7909cbf
Merge pull request #483 from avirajkmar848-png/add-skydns-resolver
jason5ng32 Sep 17, 2026
d3ada7e
Merge branch 'dev' of https://github.com/jason5ng32/MyIP into dev
jason5ng32 Sep 17, 2026
7c14697
Improvements
jason5ng32 Sep 17, 2026
b2b98b5
Fix(dns): share fallback probes within each test run
jason5ng32 Sep 17, 2026
65907c9
Fix(ipcalculator): sync share URLs after prefix changes
jason5ng32 Sep 17, 2026
45be0e7
Improvements
jason5ng32 Sep 17, 2026
39ae0ad
Merge pull request #484 from jason5ng32/dev
jason5ng32 Sep 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ jobs:
check:
name: Test + Build
runs-on: ubuntu-latest
# Normal run is ~1 min; a hosted runner that loses its connection would
# otherwise sit "in progress" for the 6h default before failing.
timeout-minutes: 10

steps:
- name: Checkout
Expand All @@ -22,7 +25,7 @@ jobs:
- name: Setup pnpm
# Reads the `packageManager` field in package.json to pin the pnpm version,
# so CI uses the exact same pnpm as local dev.
uses: pnpm/action-setup@v4
uses: pnpm/action-setup@v6

- name: Setup Node
uses: actions/setup-node@v6
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ jobs:
build-and-push:
runs-on: ubuntu-latest
environment: production
# The multi-arch build normally lands in ~5 min; a hosted runner that loses
# its connection would otherwise sit "in progress" for the 6h default.
timeout-minutes: 20

steps:
- name: Check Out Repo
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ common/as-rel-db/*.next
.learnings/
docs/
.plan/
.skills/

# Local Scripts (the listed exceptions are public — contributors need
# fetch-favicons.js for the Connectivity favicon pipeline, and the i18n pair
Expand Down
18 changes: 16 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,26 @@
Single source of truth for anyone — human or AI — contributing to MyIP.
Area-specific details: @frontend/AGENTS.md (Vue SPA) · @api/AGENTS.md (Express API).

## Local skill discovery

Before choosing a workflow for a project task, check the repository-root
`.skills/` **directory**. It contains local skills at `.skills/<name>/SKILL.md`
and may not appear in the agent's automatically supplied skill list.

- If the directory exists, discover its skill files with
`rg --files --hidden --no-ignore .skills -g SKILL.md` (it is gitignored).
- Read each skill's frontmatter `name` and `description`; read the full
`SKILL.md` for any skill that applies, then follow its workflow.
- Complete this discovery before choosing a fallback tool or telling the
user that a project capability or service is unavailable. Load supporting
files only when the selected skill requires them.

## Overview

**MyIP** (IPCheck.ing) is an open-source IP toolbox: IP lookup, connectivity
tests, WebRTC / DNS-leak detection, speed test, MTR, Whois, security
checklist, browser fingerprint, anonymity checks, persona check, and
more. Single repo, two
checklist, browser fingerprint, anonymity checks, persona check, IP
calculator, and more. Single repo, two
halves: a Vue 3 SPA front-end and an Express 5 back-end API.

## Stack
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Feel free to bookmark the demo or deploy your own.
* 📟 **DNS Resolver**: Resolves a domain through multiple resolvers at once, grouped by country — an easy way to spot hijacking or contamination.
* 📓 **Whois Search**: Performs Whois lookups for domain names and IP addresses.
* 🗄️ **MAC Lookup**: Identifies the vendor and details behind a physical address.
* 🧮 **IP Calculator**: Subnet math, notation conversions and IPv6 interface details for any IP, prefix, range or list, computed locally.
* 🛰️ **ASN Info & Upstream Topology**: Shows AS details, historical announcements for an IP prefix, and the upstream paths from an ASN to the Tier 1 backbone.
* 📶 **Service Status**: Live availability of well-known services — Claude, OpenAI, GitHub, Cloudflare, and more — from their official status pages, with recent incidents.

Expand Down
1 change: 1 addition & 0 deletions README_FR.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ N'hésitez pas à mettre la démo en favori ou à déployer votre propre instanc
* 📟 **Résolveur DNS** : Résout un domaine via plusieurs résolveurs à la fois, regroupés par pays — un moyen simple de repérer un détournement ou une contamination.
* 📓 **Recherche Whois** : Effectue des recherches Whois pour les noms de domaine et les adresses IP.
* 🗄️ **Recherche MAC** : Identifie le fabricant et les détails derrière une adresse physique.
* 🧮 **Calculateur IP** : Calcul de sous-réseau, conversions de notation et détails d'interface IPv6 pour toute IP, préfixe, plage ou liste, le tout en local.
* 🛰️ **Infos ASN et topologie amont** : Affiche les détails d'un AS, l'historique des annonces d'un préfixe IP et les chemins amont d'un ASN vers la dorsale Tier 1.
* 📶 **État des services** : Disponibilité en direct de services connus — Claude, OpenAI, GitHub, Cloudflare et d'autres — depuis leurs pages d'état officielles, avec les incidents récents.

Expand Down
1 change: 1 addition & 0 deletions README_PT-BR.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Adicione a demonstração aos favoritos ou faça sua própria implantação.
* 📟 **Resolução DNS**: Resolve um domínio por vários resolvedores de uma só vez, agrupados por país — um jeito fácil de detectar sequestro ou contaminação.
* 📓 **Pesquisa Whois**: Realiza consultas Whois para nomes de domínio e endereços IP.
* 🗄️ **Consulta de MAC**: Identifica o fabricante e os detalhes por trás de um endereço físico.
* 🧮 **Calculadora de IP**: Cálculo de sub-rede, conversões de notação e detalhes de interface IPv6 para qualquer IP, prefixo, intervalo ou lista, tudo localmente.
* 🛰️ **Informações de ASN e topologia de upstream**: Mostra detalhes do AS, anúncios históricos de um prefixo IP e os caminhos de upstream de um ASN até o backbone Tier 1.
* 📶 **Status dos serviços**: Disponibilidade em tempo real de serviços conhecidos — Claude, OpenAI, GitHub, Cloudflare e outros — a partir de suas páginas oficiais de status, com incidentes recentes.

Expand Down
1 change: 1 addition & 0 deletions README_RU.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
* 📟 **Разрешение DNS**: разрешает домен сразу через несколько резолверов, сгруппированных по странам, — простой способ заметить перехват или загрязнение DNS.
* 📓 **Поиск Whois**: выполняет Whois-запросы для доменных имён и IP-адресов.
* 🗄️ **Поиск MAC-адреса**: определяет производителя и другие сведения по физическому адресу.
* 🧮 **IP-калькулятор**: расчёт подсетей, преобразование записей и сведения об интерфейсе IPv6 для любого IP, префикса, диапазона или списка — всё локально.
* 🛰️ **Сведения об ASN и топология вышестоящих сетей**: показывают данные автономной системы, историю анонсов IP-префикса и пути от ASN к магистральным сетям Tier 1.
* 📶 **Состояние сервисов**: актуальная доступность известных сервисов — Claude, OpenAI, GitHub, Cloudflare и других — по данным их официальных страниц состояния, включая недавние инциденты.

Expand Down
1 change: 1 addition & 0 deletions README_ZH-TW.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
* 📟 **DNS 解析**:同時透過多個解析器解析網域,並依國家分組——輕鬆看出是否存在劫持或污染。
* 📓 **Whois 查詢**:對網域名稱與 IP 位址進行 Whois 查詢。
* 🗄️ **MAC 位址查詢**:識別實體位址背後的廠商與詳細資訊。
* 🧮 **IP 計算器**:對任意 IP、前綴、範圍或清單做子網路計算、進位轉換與 IPv6 介面解讀,全部本機完成。
* 🛰️ **ASN 資訊與上游拓撲**:顯示 AS 詳細資訊、IP 前綴的歷史宣告記錄,以及該 ASN 到 Tier 1 骨幹網路的上游路徑。
* 📶 **服務可用性**:知名服務(Claude、OpenAI、GitHub、Cloudflare 等)的即時可用狀態,資料來自它們的官方狀態頁,並附最近的事故。

Expand Down
1 change: 1 addition & 0 deletions README_ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
* 📟 **DNS 解析**:同时通过多个解析器解析一个域名,并按国家分组 —— 轻松发现劫持或污染。
* 📓 **Whois 查询**:对域名和 IP 地址进行 Whois 查询。
* 🗄️ **MAC 地址查询**:识别一个物理地址背后的厂商与详细信息。
* 🧮 **IP 计算器**:对任意 IP、前缀、范围或列表做子网计算、进制转换与 IPv6 接口解读,全部本地完成。
* 🛰️ **ASN 信息与上游拓扑**:展示 AS 详情、IP 前缀的历史宣告记录,以及从某个 ASN 到 Tier 1 骨干网的上游路径。
* 📶 **服务可用性**:知名服务的实时可用状态 —— Claude、OpenAI、GitHub、Cloudflare 等 —— 数据来自它们的官方状态页,并附最近的事故。

Expand Down
4 changes: 2 additions & 2 deletions TRANSLATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ can see at a glance which strings you actually wrote.
| `frontend/locales/<code>.json` | The main pack — every string in the app UI (~1,150 keys) | **Yes** — all keys, values may be `""` |
| `frontend/locales/privacy/<code>.json` | Privacy policy copy (~50 keys) | No — but whole or not at all |
| `frontend/locales/security-checklist/<code>.json` | The Cybersecurity Checklist dataset (~1,080 keys, 258 items) | No — but whole or not at all |
| `frontend/data/changelog.json` | Release history, one string per language per entry (163 entries) | No — beta languages are exempt |
| `frontend/data/changelog.json` | Release history, one string per language per entry | No — beta languages are exempt |

`en.json` is the reference for all of them: a translation may lag behind English, never
contradict it.
Expand Down Expand Up @@ -201,7 +201,7 @@ maintainer decision, made when the language is actually complete:

- Main pack, privacy copy and security checklist all at 100% against `en` — not a single
`""` left (`pnpm i18n-status` shows this).
- Changelog history back-filled — all 163 entries in `frontend/data/changelog.json`.
- Changelog history back-filled — every entry in `frontend/data/changelog.json`.
- Enough of a track record that copy changes will keep landing in it.

Once `status` flips to `'full'`, `tests/locale-packs.test.js` and `tests/changelog.test.js`
Expand Down
9 changes: 5 additions & 4 deletions api/data/dns-resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,18 @@ export const DNS_RESOLVERS = [
{ id: 'google', name: 'Google', country: 'US', udp: '8.8.8.8', doh: 'https://dns.google/resolve?' },
{ id: 'cloudflare', name: 'Cloudflare', country: 'US', udp: '1.1.1.1', doh: 'https://cloudflare-dns.com/dns-query?ct=application/dns-json&' },
{ id: 'opendns', name: 'OpenDNS', country: 'US', udp: '208.67.222.222' },
{ id: 'quad9', name: 'Quad9', country: 'CH', udp: '9.9.9.9' },
{ id: 'controld', name: 'ControlD', country: 'CA', udp: '76.76.2.0' },
{ id: 'dns4eu', name: 'DNS4EU', country: 'EU', udp: '86.54.11.1' },
{ id: 'dnssb', name: 'DNS.SB', country: 'EU', udp: '185.222.222.222', doh: 'https://doh.dns.sb/dns-query?' },
{ id: 'quad9', name: 'Quad9', country: 'CH', udp: '9.9.9.9' },
{ id: 'cznic', name: 'CZ.NIC ODVR', country: 'CZ', udp: '193.17.47.1' },
{ id: 'adguard', name: 'AdGuard', country: 'CY', udp: '94.140.14.14', doh: 'https://dns.adguard.com/resolve?' },
{ id: 'yandex', name: 'Yandex.DNS', country: 'RU', udp: '77.88.8.8' },
{ id: 'skydns', name: 'SkyDNS', country: 'RU', udp: '193.58.251.251' },
{ id: 'alidns', name: 'AliDNS', country: 'CN', udp: '223.5.5.5', doh: 'https://dns.alidns.com/resolve?' },
{ id: 'dnspod', name: 'DNSPod', country: 'CN', udp: '119.29.29.29' },
{ id: '114dns', name: '114DNS', country: 'CN', udp: '114.114.114.114' },
{ id: 'hinet', name: 'HiNet', country: 'TW', udp: '168.95.1.1' },
{ id: 'giga', name: 'GIGA', country: 'TW', udp: '203.133.1.6' },
{ id: 'dns4eu', name: 'DNS4EU', country: 'EU', udp: '86.54.11.1' },
{ id: 'cznic', name: 'CZ.NIC ODVR', country: 'CZ', udp: '193.17.47.1' },
{ id: 'dnssb', name: 'DNS.SB', country: 'EU', udp: '185.222.222.222', doh: 'https://doh.dns.sb/dns-query?' },
{ id: 'kt', name: 'KT', country: 'KR', udp: '168.126.63.1' },
];
27 changes: 9 additions & 18 deletions api/dns-resolver.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// api/dns-resolver.js — GET /api/dnsresolver: resolve a hostname against every
// resolver in api/data/dns-resolvers.js (UDP DNS + DoH) in parallel and return
// one flat, country-annotated result list the frontend groups by country.
import { Resolver } from 'dns';
import { promisify } from 'util';
import { Resolver } from 'node:dns/promises';
import { fetchUpstream } from '../common/fetch-with-timeout.js';
import logger from '../common/logger.js';
import { DNS_RESOLVERS } from './data/dns-resolvers.js';
Expand Down Expand Up @@ -79,46 +78,38 @@ export const dohRecords = (data, type) => {
const resolveDns = async (hostname, type, name, server) => {
const resolver = new Resolver({ timeout: DNS_TIMEOUT_MS, tries: 1 });
resolver.setServers([server]);
const resolve4Async = promisify(resolver.resolve4.bind(resolver));
const resolve6Async = promisify(resolver.resolve6.bind(resolver));
const resolveTxtAsync = promisify(resolver.resolveTxt.bind(resolver));
const resolveCnameAsync = promisify(resolver.resolveCname.bind(resolver));
const resolveNSAsync = promisify(resolver.resolveNs.bind(resolver));
const resolveMXAsync = promisify(resolver.resolveMx.bind(resolver));
const resolveSoaAsync = promisify(resolver.resolveSoa.bind(resolver));
const resolveCaaAsync = promisify(resolver.resolveCaa.bind(resolver));
try {
let addresses;

// Select different parsing methods based on the type parameter
switch (type) {
case 'A':
addresses = await resolve4Async(hostname);
addresses = await resolver.resolve4(hostname);
break;
case 'AAAA':
addresses = await resolve6Async(hostname);
addresses = await resolver.resolve6(hostname);
break;
case 'TXT':
addresses = await resolveTxtAsync(hostname);
addresses = await resolver.resolveTxt(hostname);
// TXT record parsing results is a two-dimensional array, here we flatten the result
addresses = addresses.flat();
break;
case 'CNAME':
addresses = await resolveCnameAsync(hostname);
addresses = await resolver.resolveCname(hostname);
break;
case 'NS':
addresses = await resolveNSAsync(hostname);
addresses = await resolver.resolveNs(hostname);
break;
case 'MX':
addresses = await resolveMXAsync(hostname);
addresses = await resolver.resolveMx(hostname);
addresses = addresses.map(item => `${item.priority} ${item.exchange}.`)
.join(', ');
break;
case 'SOA':
addresses = formatSoaRecord(await resolveSoaAsync(hostname));
addresses = formatSoaRecord(await resolver.resolveSoa(hostname));
break;
case 'CAA':
addresses = formatCaaRecords(await resolveCaaAsync(hostname));
addresses = formatCaaRecords(await resolver.resolveCaa(hostname));
break;
default:
throw new Error('Unsupported type');
Expand Down
5 changes: 2 additions & 3 deletions api/ipinfo-io.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Picks a random API token (when configured) and normalizes the response
// into the canonical geo shape via the shared makeGeoHandler factory.

import countryLookup from 'country-code-lookup';
import getCountryName from '../common/country-name.js';
import { makeGeoHandler } from '../common/geo-handler.js';

function buildUrl(req) {
Expand Down Expand Up @@ -33,8 +33,7 @@ const toCoordinate = (raw) => {
export const modifyJson = (json) => {
const { ip, city, region, country, loc, org } = json || {};

// byIso returns null for an unknown or absent code.
const countryName = countryLookup.byIso(country)?.country || 'Unknown Country';
const countryName = getCountryName(country, 'en') || 'Unknown Country';

// "37.4056,-122.0775" — a partial or non-numeric pair degrades to null.
const [rawLat, rawLon] = typeof loc === 'string' ? loc.split(',') : [];
Expand Down
31 changes: 31 additions & 0 deletions common/country-name.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Localized country/region names via the built-in Intl.DisplayNames API.
// Names come from the runtime's CLDR/ICU data, so any ISO 3166-1 alpha-2 code
// resolves in any supported locale with zero maintenance and zero bundle weight.

const displayNamesCache = new Map();

const getDisplayNames = (lang) => {
if (!displayNamesCache.has(lang)) {
let displayNames = null;
try {
// fallback: 'none' → unknown-but-well-formed codes yield undefined
// instead of echoing the code back, so callers get '' as before
displayNames = new Intl.DisplayNames([lang], { type: 'region', fallback: 'none' });
} catch {
// malformed / unsupported locale — cache the null so we don't retry
}
displayNamesCache.set(lang, displayNames);
}
return displayNamesCache.get(lang);
};

const getCountryName = (abbr, lang) => {
if (typeof abbr !== 'string' || typeof lang !== 'string') return '';
const code = abbr.toUpperCase();
// ZZ is the ISO "unknown region" placeholder; return '' so callers can fall back
if (!/^[A-Z]{2}$/.test(code) || code === 'ZZ') return '';
const displayNames = getDisplayNames(lang);
return (displayNames && displayNames.of(code)) || '';
};

export default getCountryName;
Loading
Loading