diff --git a/build/prepare-sites-build.mjs b/build/prepare-sites-build.mjs
index d842cd8..35ff203 100644
--- a/build/prepare-sites-build.mjs
+++ b/build/prepare-sites-build.mjs
@@ -10,6 +10,7 @@ const browserRoutes = [
'privacy',
'proto',
'proto/docs',
+ 'rcc',
'support',
'terms',
];
diff --git a/public/sitemap.xml b/public/sitemap.xml
index ed012af..4d22a04 100644
--- a/public/sitemap.xml
+++ b/public/sitemap.xml
@@ -8,10 +8,16 @@
https://rosetta.im/proto/
- 2026-07-30
+ 2026-08-01
weekly
0.9
+
+ https://rosetta.im/rcc/
+ 2026-08-01
+ monthly
+ 0.8
+
https://rosetta.im/proto/docs/
2026-07-31
diff --git a/src/App.tsx b/src/App.tsx
index b98b291..6978dfb 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -18,6 +18,9 @@ const ProtoPage = lazy(() =>
const ProtoDocsPage = lazy(() =>
import('./pages/ProtoPage').then((module) => ({ default: module.ProtoDocsPage })),
);
+const RccPage = lazy(() =>
+ import('./pages/RccPage').then((module) => ({ default: module.RccPage })),
+);
const SupportPage = lazy(() =>
import('./pages/SupportPage').then((module) => ({ default: module.SupportPage })),
);
@@ -87,6 +90,7 @@ export default function App() {
} />
} />
} />
+ } />
} />
} />
} />
diff --git a/src/components/SiteFooter/SiteFooter.tsx b/src/components/SiteFooter/SiteFooter.tsx
index 36f2628..8e8a64c 100644
--- a/src/components/SiteFooter/SiteFooter.tsx
+++ b/src/components/SiteFooter/SiteFooter.tsx
@@ -39,6 +39,9 @@ export function SiteFooter() {
Proto
+
+ RCC
+
Support
diff --git a/src/pages/ProtoPage.module.css b/src/pages/ProtoPage.module.css
index 3c75367..eba4502 100644
--- a/src/pages/ProtoPage.module.css
+++ b/src/pages/ProtoPage.module.css
@@ -26,6 +26,11 @@
font-weight: 650;
}
+.important {
+ color: light-dark(var(--mantine-color-orange-8), var(--mantine-color-orange-3));
+ font-weight: 650;
+}
+
.layerExamples {
min-width: 0;
margin: 18px 0 24px;
@@ -40,9 +45,13 @@
margin-bottom: 0;
}
+.docsLinks {
+ margin-top: 44px;
+ gap: 28px;
+}
+
.docsLink {
display: inline-block;
- margin-top: 44px;
font-size: var(--mantine-font-size-sm);
font-weight: 600;
text-decoration: none;
diff --git a/src/pages/ProtoPage.tsx b/src/pages/ProtoPage.tsx
index dd3d913..98322c8 100644
--- a/src/pages/ProtoPage.tsx
+++ b/src/pages/ProtoPage.tsx
@@ -517,6 +517,57 @@ export class RccGeneratedPacketRegistry {
+
+
+ Rosetta Code Compiler (RCC) is the build-time compiler behind the protocol.
+ It reads every layer.N.yml snapshot, turns the YAML into a
+ typed protocol model, validates that the history is safe, and emits source
+ code for the selected language. Applications import generated models and
+ codecs; they do not parse the YAML at runtime.
+
+
+ {`proto/layer.*.yml
+ │
+ ├─ parse packets, reusable types, enums and comments
+ ├─ validate references, required fields and stable field IDs
+ │
+ ├─ mode=server → all layers + version-aware codecs + registry
+ └─ mode=client → latest layer + latest codecs + version constant`}
+
+
+
+ Server generation preserves the protocol timeline.
+ Java codecs receive the negotiated layer, include only fields active in
+ that layer, and expose the compiled minimum and maximum versions.
+
+
+ Client generation{' '}
+
+ selects the highest layer and creates the current packet, type, enum,
+ codec, and registry sources without carrying historical shapes into
+ the application.
+
+
+
+ Schema validation fails the build for unknown types or
+ unsafe field-ID reuse, so incompatible protocol edits are caught before
+ generated sources reach a server or client package.
+
+
+ Generated documentation carries schema comments into
+ Javadoc or TSDoc, including field direction, wire type, optionality, and
+ accessor contracts.
+
+
+
+ The RCC guide includes separate client and version-aware server examples.{' '}
+
+ Run npx rcc --list-languages for the authoritative list of
+ language targets in the installed compiler version.
+
+
+
+
RCC reads versioned files named layer.N.yml. The top-level
@@ -664,9 +715,14 @@ packet field 1 · INT8 · SUCCESS (0)`}
-
- See docs of PROTO →
-
+
+
+ See docs of PROTO →
+
+
+ Learn RCC →
+
+
>
);
diff --git a/src/pages/RccPage.module.css b/src/pages/RccPage.module.css
new file mode 100644
index 0000000..4e587ff
--- /dev/null
+++ b/src/pages/RccPage.module.css
@@ -0,0 +1,114 @@
+.code {
+ margin: 18px 0;
+ padding: 16px;
+ border: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-5));
+ border-radius: var(--mantine-radius-xs);
+ background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7));
+ color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0));
+ font-size: 12px;
+ line-height: 1.65;
+ overflow-x: auto;
+ white-space: pre;
+}
+
+.pipeline {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr) auto minmax(0, 1fr);
+ gap: 12px;
+ align-items: center;
+ margin: 22px 0;
+}
+
+.pipeline > span:nth-child(odd) {
+ padding: 14px;
+ border: 1px solid light-dark(var(--mantine-color-blue-2), var(--mantine-color-blue-9));
+ border-radius: var(--mantine-radius-xs);
+ background: light-dark(var(--mantine-color-blue-0), rgba(25, 113, 194, 0.12));
+ color: light-dark(var(--mantine-color-blue-9), var(--mantine-color-blue-1));
+ font-size: var(--mantine-font-size-sm);
+ font-weight: 650;
+ line-height: 1.45;
+ text-align: center;
+}
+
+.pipeline > span:nth-child(even) {
+ color: var(--mantine-color-blue-6);
+ font-weight: 700;
+}
+
+.step {
+ display: grid;
+ grid-template-columns: 34px minmax(0, 1fr);
+ gap: 16px;
+ margin-top: 30px;
+}
+
+.step:first-child {
+ margin-top: 0;
+}
+
+.stepNumber {
+ display: grid;
+ width: 34px;
+ height: 34px;
+ place-items: center;
+ border-radius: 50%;
+ background: var(--mantine-color-blue-6);
+ color: var(--mantine-color-white);
+ font-size: var(--mantine-font-size-sm);
+ font-weight: 700;
+}
+
+.stepBody {
+ min-width: 0;
+ padding-bottom: 24px;
+ border-bottom: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
+}
+
+.step:last-child .stepBody {
+ padding-bottom: 0;
+ border-bottom: 0;
+}
+
+.subheading {
+ margin: 3px 0 12px;
+ color: light-dark(var(--mantine-color-dark-8), var(--mantine-color-dark-0));
+ font-size: 17px;
+ font-weight: 650;
+}
+
+.important {
+ color: light-dark(var(--mantine-color-orange-8), var(--mantine-color-orange-3));
+ font-weight: 650;
+}
+
+.note {
+ margin: 18px 0;
+ border: 1px solid light-dark(var(--mantine-color-orange-3), var(--mantine-color-orange-9));
+ border-left: 3px solid var(--mantine-color-orange-6);
+ background: light-dark(var(--mantine-color-orange-0), rgba(253, 126, 20, 0.1));
+ color: light-dark(var(--mantine-color-orange-9), var(--mantine-color-orange-2));
+}
+
+@media (max-width: 48em) {
+ .pipeline {
+ grid-template-columns: minmax(0, 1fr);
+ }
+
+ .pipeline > span:nth-child(even) {
+ transform: rotate(90deg);
+ text-align: center;
+ }
+}
+
+@media (max-width: 36em) {
+ .step {
+ grid-template-columns: 28px minmax(0, 1fr);
+ gap: 12px;
+ }
+
+ .stepNumber {
+ width: 28px;
+ height: 28px;
+ }
+}
diff --git a/src/pages/RccPage.tsx b/src/pages/RccPage.tsx
new file mode 100644
index 0000000..e24d926
--- /dev/null
+++ b/src/pages/RccPage.tsx
@@ -0,0 +1,239 @@
+import { Anchor, Box, Code, List, Paper, Text, Title } from '@mantine/core';
+import type { ReactNode } from 'react';
+import {
+ LegalList,
+ LegalPage,
+ LegalSection,
+ LegalText,
+} from '../components/LegalPage/LegalPage';
+import { SEO } from '../components/SEO/SEO';
+import classes from './RccPage.module.css';
+
+const GITEA_REPOSITORY = 'https://git.rosetta.im/Rosetta/rcc';
+
+function TechnicalCode({ children }: { children: string }) {
+ return (
+
+ {children}
+
+ );
+}
+
+function Step({ number, title, children }: { number: number; title: string; children: ReactNode }) {
+ return (
+
+ {number}
+
+
+ {title}
+
+ {children}
+
+
+ );
+}
+
+export function RccPage() {
+ return (
+ <>
+
+
+
+ RCC compiles Rosetta's versioned YAML protocol into typed models, codecs,
+ and packet registries.{' '}
+
+ A server build can retain the complete protocol history; a client build
+ stays small by generating only the latest layer.
+
+
+
+
+
+ RCC treats proto/layer.*.yml as the protocol's source of
+ truth. Before writing code, it resolves primitive, enum, and reusable type
+ references and rejects incompatible reuse of a field ID between layers.
+
+
+ Versioned YAML schemas
+ →
+ Parse + validate
+ →
+ Models + codecs + registry
+
+
+
+ Packets become typed packet classes; reusable schema types become model
+ classes; enums preserve their numeric wire values.
+
+
+ Codecs encode and decode TLV fields and verify required fields before
+ encoding and after decoding.
+
+
+ The generated registry maps packet IDs to codecs and exposes the
+ compiled protocol version or version range.
+
+
+ YAML comments become Javadoc or TSDoc for generated declarations and
+ accessors.
+
+
+
+
+
+
+ Add Rosetta's npm package registry to the project, then install RCC. The
+ registry setting can live in a project-level .npmrc so CI and
+ local builds resolve the same compiler package.
+
+ {`# .npmrc
+registry=https://git.rosetta.im/api/packages/Rosetta/npm/
+
+# install the compiler
+npm install rcc
+
+# query languages from the installed RCC version
+npx rcc --list-languages
+
+# inspect the rest of the CLI
+npx rcc --help`}
+
+ RCC source is hosted in the{' '}
+
+ Rosetta/rcc Gitea repository
+
+ .{' '}
+
+ Pin the dependency version in the project lockfile so generated output
+ remains reproducible.
+
+
+
+
+
+
+
+ Put complete protocol snapshots in one directory and name them
+ layer.N.yml. Increment the top-level version, keep packet
+ and field IDs stable, and repeat definitions that remain part of the
+ protocol.
+
+ {`proto/
+├── layer.1.yml
+├── layer.2.yml
+└── layer.3.yml`}
+
+
+
+
+ Use client for the current application package. RCC selects
+ the highest schema version. Use server when the runtime must
+ decode connections negotiated on older supported layers. Full
+ multi-version codec generation is currently implemented by the Java
+ backend.
+
+
+
+
+
+ Server generation combines all layers and emits version-aware codecs,
+ models, enums, and a registry with MIN_VERSION and
+ MAX_VERSION.
+
+ {`npx rcc \\
+ --mode server \\
+ --lang java \\
+ --protoDir ./proto \\
+ --outDir ./server/src/generated/java \\
+ --basePackage im.rosetta.network \\
+ --clean`}
+
+ For a Java client, change the mode and output directory. The generated
+ sources describe only the latest layer.
+
+ {`npx rcc \\
+ --mode client \\
+ --lang java \\
+ --protoDir ./proto \\
+ --outDir ./client/src/generated/java \\
+ --basePackage im.rosetta.network \\
+ --clean`}
+
+
+
+
+ TypeScript is the current latest-layer client target. It produces
+ models, enums, codecs, and RccGeneratedPacketRegistry.VERSION.
+
+ {`npx rcc \\
+ --mode client \\
+ --lang typescript \\
+ --protoDir ./proto \\
+ --outDir ./client/src/generated/protocol \\
+ --clean`}
+
+
+ Always run npx rcc --list-languages before configuring a
+ generator. The installed compiler's output is the source of truth
+ for available language targets.
+
+
+
+
+
+
+ Keep client and server generation together in
+ rcc-prebuild.yml. Paths are resolved relative to the
+ config file; clean: true clears only each configured output
+ directory.
+
+ {`watch: false
+protoDir: ./proto
+clean: true
+
+compilers:
+ - name: TypeScript client
+ mode: client
+ lang: typescript
+ output: ./client/src/generated/protocol
+
+ - name: Java server
+ mode: server
+ lang: java
+ output: ./server/src/generated/java
+ basePackage: im.rosetta.network`}
+ {`npx rcc -f ./rcc-prebuild.yml`}
+
+
+
+
+ Add the output directory to the language build, then run generation
+ before compilation in local development and CI. RCC writes source
+ files; the surrounding Gradle, npm, or application build is responsible
+ for compiling and publishing the final server or client package.
+
+
+
+
+
+ {`npx rcc --mode [options]
+npx rcc -f
+npx rcc --list-languages
+
+--mode, -m client | server
+--lang, -l language returned by --list-languages
+--protoDir, -p schema directory (default: ./proto)
+--outDir, -o generated source directory (default: ./generated)
+--basePackage, -b Java base package (default: im.rosetta.generated)
+--clean clean the output directory before generation
+--file, -f prebuild YAML config`}
+
+
+ >
+ );
+}