diff --git a/CHANGELOG.md b/CHANGELOG.md
index 287a8b7..290103f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
Routine `stats.json` refreshes take a PATCH version bump but are grouped rather than
listed individually.
+## [0.26.0] - 2026-09-21
+
+### Added
+- `/platform` and `/ko/platform`: the layer above the three lines — the entity layer (11,953 entities, measured 2026-09-21), sanctions screening, the corporate registry and the consortium exchange, with the four-step hash-and-band protocol and a *What is true today* list that says which parts are live, built or waiting on a member. Nav entry in all three locales; sitemap. Written against `opsight-company/roadmaps/platform-direction.md`; Korean by the agent, needs a read.
+
## [0.25.0] - 2026-09-21
### Added
diff --git a/VERSION b/VERSION
index 5d60147..4e8f395 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.25.1
\ No newline at end of file
+0.26.0
diff --git a/src/components/Platform.astro b/src/components/Platform.astro
new file mode 100644
index 0000000..a0b17cd
--- /dev/null
+++ b/src/components/Platform.astro
@@ -0,0 +1,53 @@
+---
+import type { PlatformCopy } from '../i18n/platform';
+import HeroBand from './HeroBand.astro';
+import ContactPanel from './ContactPanel.astro';
+interface Props { copy: PlatformCopy; locale: 'en' | 'ko' }
+const { copy, locale } = Astro.props;
+---
+
+
+
+ {copy.question.heading}
+ {copy.question.lead}
+
+
{copy.question.before}
+
{copy.question.after}
+
+
+
+
+ {copy.layers.heading}
+ {copy.layers.lead}
+
+ {copy.layers.items.map((l, i) => (
+
{String(i + 1).padStart(2, '0')}{l.title}
{l.body}
{l.state}
+ ))}
+
+
+
+
+ {copy.exchange.heading}
+ {copy.exchange.lead}
+
+ {copy.exchange.steps.map((s, i) => (
+
{String(i + 1).padStart(2, '0')}{s.title}
{s.body}
+ ))}
+
+
+ {copy.exchange.never.map((n) => - {n}
)}
+
+
+
+
+ {copy.honesty.heading}
+
+ {copy.honesty.items.map((i) => - {i}
)}
+
+
+
+
+ {copy.ask.heading}
+ {copy.ask.body}
+
+
diff --git a/src/i18n/common.ts b/src/i18n/common.ts
index d138a01..2853140 100644
--- a/src/i18n/common.ts
+++ b/src/i18n/common.ts
@@ -7,7 +7,7 @@ export type Locale = 'en' | 'ko' | 'tr';
export interface CommonCopy {
htmlLang: string;
ogLocale: string;
- nav: { brand: string; fraud: string; procurement: string; manufacturing: string; opsentry: string; method: string; about: string };
+ nav: { brand: string; fraud: string; procurement: string; manufacturing: string; opsentry: string; platform: string; method: string; about: string };
langNames: Record;
footer: { rights: string; method: string };
live: { badge: string; lastWritten: string; source: string };
@@ -18,7 +18,7 @@ export const common: Record = {
en: {
htmlLang: 'en',
ogLocale: 'en_US',
- nav: { brand: 'OPSIGHT INTELLIGENCE', fraud: 'Fraud', procurement: 'Procurement', manufacturing: 'Manufacturing', opsentry: 'OpSentry', method: 'Method', about: 'About' },
+ nav: { brand: 'OPSIGHT INTELLIGENCE', fraud: 'Fraud', procurement: 'Procurement', manufacturing: 'Manufacturing', opsentry: 'OpSentry', platform: 'Platform', method: 'Method', about: 'About' },
langNames: { en: 'English', ko: '한국어', tr: 'Türkçe' },
footer: { rights: '© 2026 Opsight Intelligence. All rights reserved.', method: 'Built on public and free-tier sources.' },
live: { badge: 'Live', lastWritten: 'last written', source: 'source' },
@@ -27,7 +27,7 @@ export const common: Record = {
ko: {
htmlLang: 'ko',
ogLocale: 'ko_KR',
- nav: { brand: 'OPSIGHT INTELLIGENCE', fraud: '사기 인텔리전스', procurement: '공공조달', manufacturing: '제조', opsentry: 'OpSentry', method: '방법론', about: '소개' },
+ nav: { brand: 'OPSIGHT INTELLIGENCE', fraud: '사기 인텔리전스', procurement: '공공조달', manufacturing: '제조', opsentry: 'OpSentry', platform: '플랫폼', method: '방법론', about: '소개' },
langNames: { en: 'English', ko: '한국어', tr: 'Türkçe' },
footer: { rights: '© 2026 Opsight Intelligence. All rights reserved.', method: '공개 및 무료 데이터 소스로 구축했습니다.' },
live: { badge: '실시간', lastWritten: '작성일', source: '출처' },
@@ -36,7 +36,7 @@ export const common: Record = {
tr: {
htmlLang: 'tr',
ogLocale: 'tr_TR',
- nav: { brand: 'OPSIGHT INTELLIGENCE', fraud: 'Dolandırıcılık', procurement: 'Kamu İhaleleri', manufacturing: 'Üretim', opsentry: 'OpSentry', method: 'Yöntem', about: 'Hakkında' },
+ nav: { brand: 'OPSIGHT INTELLIGENCE', fraud: 'Dolandırıcılık', procurement: 'Kamu İhaleleri', manufacturing: 'Üretim', opsentry: 'OpSentry', platform: 'Platform', method: 'Yöntem', about: 'Hakkında' },
langNames: { en: 'English', ko: '한국어', tr: 'Türkçe' },
footer: { rights: '© 2026 Opsight Intelligence. Tüm hakları saklıdır.', method: 'Açık ve ücretsiz kaynaklar üzerine kuruludur.' },
live: { badge: 'Canlı', lastWritten: 'yazıldı', source: 'kaynak' },
diff --git a/src/i18n/platform.ts b/src/i18n/platform.ts
new file mode 100644
index 0000000..2040de6
--- /dev/null
+++ b/src/i18n/platform.ts
@@ -0,0 +1,122 @@
+// Platform page copy — /platform, /ko/platform. The layer above the three
+// lines: one entity across domains, screened nightly against the sanctions
+// lists, enriched from the corporate registry, and a consortium exchange
+// that shares hashes and bands, never values. Written against
+// opsight-company/roadmaps/platform-direction.md. Numbers are stated as
+// measured on the date given; no client, no internal package name. Korean by
+// the agent, needs a read.
+
+export type PlatformLocale = 'en' | 'ko';
+export interface Block { title: string; body: string }
+export interface PlatformCopy {
+ title: string; description: string;
+ hero: { eyebrow: string; title: string; lead: string };
+ question: { heading: string; lead: string; before: string; after: string };
+ layers: { heading: string; lead: string; items: (Block & { state: string })[] };
+ exchange: { heading: string; lead: string; steps: Block[]; never: string[] };
+ honesty: { heading: string; items: string[] };
+ ask: { heading: string; body: string };
+}
+
+export const platform: Record = {
+ en: {
+ title: 'Platform — Opsight Intelligence',
+ description: 'One entity across fraud and procurement, screened nightly against the sanctions lists, enriched from the corporate registry, and a consortium exchange that shares hashes and bands — never an account number.',
+ hero: {
+ eyebrow: 'Platform',
+ title: 'The same firm, seen from every line at once.',
+ lead: 'Three lines collect. The platform joins what they publish into one entity per real-world thing, checks every entity against the lists that matter, and lets institutions corroborate a mule signal without disclosing a customer.',
+ },
+ question: {
+ heading: 'The question it answers',
+ lead: 'The one a buyer recognises.',
+ before: 'Before: a firm that keeps losing public tenders sits in one dataset; the bank account a mule channel posts sits in another; the sanctions list is a spreadsheet someone checks by hand. Three facts about one thing, and nothing joins them.',
+ after: 'After: “This bidder that never wins — is its business number, its address or its CEO shared with a firm a fraud channel names, and is any wallet it touches on the OFAC list?” is one lookup, with every link carrying the record that justified it.',
+ },
+ layers: {
+ heading: 'Four layers, shipped',
+ lead: 'Each was built in a day on the same rule: deterministic, cited, never a similarity score. What each one states about itself is measured, not projected.',
+ items: [
+ { title: 'Entity layer', body: 'One entity per firm, account, bank account, wallet, phone or agency, resolved by its identifiers — a 사업자등록번호, a handle, an address on a chain. Typed links between them: bids on, same as, same address, shares officer. Every link carries the artifact that justified it; a person is never a node.', state: '11,953 entities from 14,144 published artifacts, measured 2026-09-21' },
+ { title: 'Sanctions screening', body: 'Every entity, every night, against OFAC’s SDN list (including its digital-currency addresses) and the UN consolidated list; EU and 전략물자 next. A hit is a key equality or an exact normalised-name equality on a corporation — never fuzzy, and never on a person. Each hit is a finding that names the list entry.', state: 'built; switched on per member, with the fetch time on every finding' },
+ { title: 'Corporate registry', body: 'For every corporate bidder, its OpenDART profile — 법인등록번호, ticker, CEO, registered address, phone — matched by name and confirmed only by the business number DART itself returns. Two bidders with one CEO or one address become a link you can cite.', state: 'built; profiles fill at up to 300 a night' },
+ { title: 'Consortium exchange', body: 'Members contribute keyed hashes of counterparties their own controls flagged, with counts and amounts as bands. A key two or more members contributed is a corroboration. Opsight contributes what fraud channels post, hashed the same way. No member ever sees another member’s rows.', state: 'built; the first member’s file is the first corroboration' },
+ ],
+ },
+ exchange: {
+ heading: 'How the exchange keeps a customer out of it',
+ lead: 'What crosses the line is a hash and a band. The protocol, in four steps.',
+ steps: [
+ { title: 'Hash on your side', body: 'A one-file, standard-library script runs inside your perimeter: each counterparty identifier is canonicalised and replaced by an HMAC over it with a pepper the consortium shares out-of-band. The value and the pepper never leave.' },
+ { title: 'Bands, not numbers', body: 'Sender counts become 1 / 2–5 / 6–20 / 21+; amounts become four KRW bands. A row cannot be inverted into a customer.' },
+ { title: 'Corroborate', body: 'The hub joins hashes across members. Two or more distinct members on one hash is a corroboration; one member is nothing.' },
+ { title: 'Read back by your own hash', body: 'A corroboration says how many members and which signals — fan-in, fragmenting, intel mention — and the window. It never says which other member.' },
+ ],
+ never: [
+ 'No raw identifier is stored on the hub; the pepper is in no table.',
+ 'No member sees another member’s contributions.',
+ 'A key one member contributed is never a finding.',
+ ],
+ },
+ honesty: {
+ heading: 'What is true today',
+ items: [
+ 'The entity layer is live and re-run nightly; its first cross-domain links appear as the fraud line publishes typed identifiers.',
+ 'Sanctions screening and the registry are built and tested against the real list and API formats; each is switched on by a member, not by default.',
+ 'The exchange has one member — Opsight — and therefore zero corroborations, by construction. The product is the protocol and the running hub.',
+ 'Nothing on this page is a similarity score. Every link, hit and corroboration can be traced to a record.',
+ ],
+ },
+ ask: { heading: 'Ask for a walk-through', body: 'A thirty-minute session over the gateway: one lookup, every layer, on your own question.' },
+ },
+ ko: {
+ title: '플랫폼 — Opsight Intelligence',
+ description: '사기·공공조달 데이터를 하나의 엔터티로 연결하고, 매일 밤 제재 명단과 대조하며, 기업 등기 정보로 보강하고, 계좌번호 대신 해시와 구간만 교환하는 컨소시엄 교환.',
+ hero: {
+ eyebrow: '플랫폼',
+ title: '같은 회사를, 모든 라인에서 동시에.',
+ lead: '세 개의 라인이 수집합니다. 플랫폼은 각 라인이 발행한 것을 실제 대상 하나당 하나의 엔터티로 합치고, 모든 엔터티를 중요한 명단과 대조하며, 기관들이 고객 정보를 공개하지 않고도 자금세탁 신호를 교차 확인하게 합니다.',
+ },
+ question: {
+ heading: '답하는 질문',
+ lead: '구매자가 바로 알아보는 그 질문.',
+ before: '이전: 공공입찰에서 계속 떨어지는 회사는 한 데이터셋에, 사기 채널이 올린 계좌번호는 다른 데이터셋에, 제재 명단은 누군가 수작업으로 확인하는 스프레드시트에 있었습니다. 한 대상에 대한 세 가지 사실이 서로 연결되지 않았습니다.',
+ after: '이후: “한 번도 낙찰되지 않는 이 입찰자 — 사업자등록번호, 주소, 대표자가 사기 채널이 언급한 회사와 같은가, 관련 지갑이 OFAC 명단에 있는가”가 한 번의 조회가 됩니다. 모든 연결은 근거가 된 기록을 함께 가집니다.',
+ },
+ layers: {
+ heading: '네 개의 계층, 출시 완료',
+ lead: '각 계층은 같은 원칙으로 하루 만에 만들어졌습니다. 결정적이고, 출처가 있으며, 유사도 점수는 쓰지 않습니다. 각 계층이 스스로에 대해 말하는 수치는 측정값이지 전망이 아닙니다.',
+ items: [
+ { title: '엔터티 계층', body: '회사·계정·계좌·지갑·전화번호·수요기관마다 하나의 엔터티. 사업자등록번호, 핸들, 체인 주소 같은 식별자로 결정됩니다. 유형이 있는 연결: 입찰, 동일 주체, 동일 주소, 임원 공유. 모든 연결은 근거 기록을 가지며, 개인은 절대 노드가 되지 않습니다.', state: '2026-09-21 기준 발행 아티팩트 14,144건에서 엔터티 11,953건' },
+ { title: '제재 스크리닝', body: '모든 엔터티를 매일 밤 OFAC SDN 명단(가상자산 주소 포함)과 UN 통합 명단에 대조합니다. EU와 전략물자 명단이 다음입니다. 적중은 키 일치 또는 법인의 정규화된 이름 완전 일치뿐입니다 — 퍼지 매칭 없음, 개인 대상 없음. 각 적중은 명단 항목을 명시한 파인딩이 됩니다.', state: '구축 완료. 회원별로 활성화하며, 모든 파인딩에 명단 수집 시각이 표시됩니다' },
+ { title: '기업 등기', body: '모든 법인 입찰자에 대해 OpenDART 프로필 — 법인등록번호, 종목코드, 대표자, 등록 주소, 전화. 이름으로 후보를 찾고 DART가 직접 반환하는 사업자등록번호로만 확정합니다. 대표자나 주소가 같은 두 입찰자는 인용 가능한 연결이 됩니다.', state: '구축 완료. 프로필은 하룻밤 최대 300건씩 채워집니다' },
+ { title: '컨소시엄 교환', body: '회원사는 자체 통제로 포착한 거래상대방의 키 해시를 건수와 금액 구간과 함께 기여합니다. 두 개 이상의 회원사가 기여한 키는 교차 확인이 됩니다. Opsight는 사기 채널이 올린 정보를 같은 방식으로 해시해 기여합니다. 어떤 회원사도 다른 회원사의 행을 볼 수 없습니다.', state: '구축 완료. 첫 회원사의 파일이 첫 교차 확인입니다' },
+ ],
+ },
+ exchange: {
+ heading: '교환에서 고객 정보를 배제하는 방법',
+ lead: '경계를 넘는 것은 해시와 구간뿐입니다. 네 단계 프로토콜.',
+ steps: [
+ { title: '귀사 내부에서 해시', body: '표준 라이브러리만 쓰는 한 파일 스크립트가 귀사 경계 안에서 실행됩니다. 거래상대방 식별자를 정규화한 뒤, 컨소시엄이 오프라인으로 공유한 페퍼로 HMAC 처리합니다. 값과 페퍼는 밖으로 나가지 않습니다.' },
+ { title: '숫자가 아닌 구간', body: '송금인 수는 1 / 2–5 / 6–20 / 21+로, 금액은 원화 네 구간으로 바뀝니다. 행 하나로 고객을 역추적할 수 없습니다.' },
+ { title: '교차 확인', body: '허브가 회원사 간 해시를 결합합니다. 한 해시에 서로 다른 회원사 둘 이상이면 교차 확인, 하나면 아무것도 아닙니다.' },
+ { title: '자신의 해시로 조회', body: '교차 확인은 회원사 수와 신호 종류(집중 입금, 분할 송금, 인텔 언급), 기간을 알려줍니다. 어느 회원사인지는 절대 말하지 않습니다.' },
+ ],
+ never: [
+ '허브에는 원본 식별자가 저장되지 않으며, 페퍼는 어떤 테이블에도 없습니다.',
+ '회원사는 다른 회원사의 기여를 볼 수 없습니다.',
+ '한 회원사만 기여한 키는 파인딩이 되지 않습니다.',
+ ],
+ },
+ honesty: {
+ heading: '오늘 기준으로 사실인 것',
+ items: [
+ '엔터티 계층은 운영 중이며 매일 밤 다시 실행됩니다. 사기 라인이 유형화된 식별자를 발행하면서 첫 교차 도메인 연결이 나타납니다.',
+ '제재 스크리닝과 기업 등기는 실제 명단과 API 형식으로 테스트를 마쳤습니다. 각각 기본값이 아니라 회원사가 활성화합니다.',
+ '교환에는 회원사가 하나(Opsight)뿐이므로 교차 확인은 정의상 0건입니다. 제품은 프로토콜과 가동 중인 허브입니다.',
+ '이 페이지의 어떤 것도 유사도 점수가 아닙니다. 모든 연결·적중·교차 확인은 기록으로 추적할 수 있습니다.',
+ ],
+ },
+ ask: { heading: '시연을 요청하세요', body: '게이트웨이를 통한 30분 세션: 귀사의 질문 하나로 모든 계층을 한 번에 조회합니다.' },
+ },
+};
diff --git a/src/layouts/Base.astro b/src/layouts/Base.astro
index 00c06b6..4e23d34 100644
--- a/src/layouts/Base.astro
+++ b/src/layouts/Base.astro
@@ -10,7 +10,7 @@ interface Props {
description: string;
path: string; // locale-less path of this page, e.g. '/' or '/fraud'
locales: Locale[]; // locales this page exists in (drives hreflang + switch)
- current: 'home' | 'fraud' | 'procurement' | 'manufacturing' | 'opsentry' | 'method' | 'about';
+ current: 'home' | 'fraud' | 'procurement' | 'manufacturing' | 'opsentry' | 'platform' | 'method' | 'about';
}
const { locale, title, description, path, locales, current } = Astro.props;
const c = common[locale];
@@ -57,6 +57,7 @@ const ogImage = `${site}/og/${current}.png`;
{c.nav.procurement}
{c.nav.manufacturing}
{c.nav.opsentry}
+ {c.nav.platform}
{c.nav.method}
{c.nav.about}
{others.map((l) => (
diff --git a/src/pages/ko/platform.astro b/src/pages/ko/platform.astro
new file mode 100644
index 0000000..83156e7
--- /dev/null
+++ b/src/pages/ko/platform.astro
@@ -0,0 +1,9 @@
+---
+import Base from '../../layouts/Base.astro';
+import Platform from '../../components/Platform.astro';
+import { platform } from '../../i18n/platform';
+const copy = platform.ko;
+---
+
+
+
diff --git a/src/pages/platform.astro b/src/pages/platform.astro
new file mode 100644
index 0000000..8c4e6be
--- /dev/null
+++ b/src/pages/platform.astro
@@ -0,0 +1,9 @@
+---
+import Base from '../layouts/Base.astro';
+import Platform from '../components/Platform.astro';
+import { platform } from '../i18n/platform';
+const copy = platform.en;
+---
+
+
+