From 179e2e65c66018e49ae2334997de93104680a6f0 Mon Sep 17 00:00:00 2001 From: rosetta Date: Thu, 30 Jul 2026 18:16:03 -0700 Subject: [PATCH] Add encryption reference and refine documentation --- public/sitemap.xml | 6 + scripts/generate-proto-docs.mjs | 8 +- src/App.tsx | 4 + src/components/LegalPage/LegalPage.module.css | 54 +- src/components/LegalPage/LegalPage.tsx | 4 +- src/components/SiteFooter/SiteFooter.tsx | 7 +- .../SiteHeader/SiteHeader.module.css | 3 +- src/components/SiteHeader/SiteHeader.tsx | 2 + src/generated/protoDocs.ts | 156 +++++- src/pages/EncryptionPage.module.css | 162 ++++++ src/pages/EncryptionPage.tsx | 511 ++++++++++++++++++ src/pages/LandingPage.module.css | 16 +- src/pages/LandingPage.tsx | 28 +- src/pages/ProtoPage.module.css | 14 +- src/pages/ProtoPage.tsx | 77 ++- src/pages/SupportPage.module.css | 14 +- src/pages/SupportPage.tsx | 10 +- src/services/updates.ts | 2 +- src/types/proto.ts | 3 + 19 files changed, 957 insertions(+), 124 deletions(-) create mode 100644 src/pages/EncryptionPage.module.css create mode 100644 src/pages/EncryptionPage.tsx diff --git a/public/sitemap.xml b/public/sitemap.xml index 00bc0d7..ae7b312 100644 --- a/public/sitemap.xml +++ b/public/sitemap.xml @@ -12,6 +12,12 @@ weekly 0.9 + + https://rosetta.im/encryption/ + 2026-07-30 + monthly + 0.9 + https://rosetta.im/support/ 2026-07-30 diff --git a/scripts/generate-proto-docs.mjs b/scripts/generate-proto-docs.mjs index 71f198c..f3284aa 100644 --- a/scripts/generate-proto-docs.mjs +++ b/scripts/generate-proto-docs.mjs @@ -1,7 +1,7 @@ import { access, mkdir, readFile, readdir, writeFile } from 'node:fs/promises'; import { basename, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; -import { isMap, isSeq, parseDocument } from 'yaml'; +import { isMap, isSeq, parseDocument, stringify } from 'yaml'; const projectRoot = resolve(fileURLToPath(new URL('..', import.meta.url))); const sourceDirectory = resolve( @@ -74,6 +74,10 @@ function mapNamedCollection(node, kind) { pair.value?.commentBefore, pair.value?.comment, ); + const yaml = stringify( + { [name]: pair.value.toJSON() }, + { indent: 2, lineWidth: 0 }, + ).trimEnd(); if (kind === 'enum') { const values = pair.value.get('values', true); @@ -81,6 +85,7 @@ function mapNamedCollection(node, kind) { name, type: String(pair.value.get('type')), description, + yaml, values: isSeq(values) ? values.items.map((value) => { const json = value.toJSON(); @@ -105,6 +110,7 @@ function mapNamedCollection(node, kind) { name, id: kind === 'packet' ? Number(pair.value.get('id')) : undefined, description, + yaml, fields, }; }); diff --git a/src/App.tsx b/src/App.tsx index 53deb6e..a354fe9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -9,6 +9,9 @@ import './style.css'; const LandingPage = lazy(() => import('./pages/LandingPage').then((module) => ({ default: module.LandingPage })), ); +const EncryptionPage = lazy(() => + import('./pages/EncryptionPage').then((module) => ({ default: module.EncryptionPage })), +); const ProtoPage = lazy(() => import('./pages/ProtoPage').then((module) => ({ default: module.ProtoPage })), ); @@ -46,6 +49,7 @@ export default function App() { > } /> + } /> } /> } /> } /> diff --git a/src/components/LegalPage/LegalPage.module.css b/src/components/LegalPage/LegalPage.module.css index e864263..c51d235 100644 --- a/src/components/LegalPage/LegalPage.module.css +++ b/src/components/LegalPage/LegalPage.module.css @@ -2,36 +2,42 @@ display: flex; flex-direction: column; min-height: 100vh; - background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-8)); + background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-9)); } .shell { flex: 1; + padding-top: 72px; + padding-bottom: 96px; } .main { width: 100%; - padding: 96px 0 112px; + max-width: 900px; + margin-inline: auto; + padding: clamp(32px, 6vw, 72px); + border-color: light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); + background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-8)); } .intro { - max-width: 900px; - padding-bottom: 64px; + max-width: 760px; + padding-bottom: 48px; + border-bottom: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); } .title { margin-top: var(--mantine-spacing-sm); color: light-dark(var(--mantine-color-black), var(--mantine-color-white)); - font-family: Outfit, var(--mantine-font-family); - font-size: clamp(40px, 7vw, 62px); - font-weight: 500; + font-size: clamp(38px, 6vw, 56px); + font-weight: 600; letter-spacing: -0.03em; line-height: 1.08; } .lead { max-width: 780px; - margin: 0; + margin: 40px 0 0; color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0)); font-size: 20px; line-height: 1.6; @@ -63,25 +69,22 @@ } .section { - display: grid; - grid-template-columns: minmax(220px, 0.42fr) minmax(0, 1fr); - gap: 52px; - margin-top: 56px; - padding-top: 48px; + margin-top: 44px; + padding-top: 36px; border-top: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); } .sectionTitle { margin: 0; color: light-dark(var(--mantine-color-dark-9), var(--mantine-color-white)); - font-family: Outfit, var(--mantine-font-family); - font-size: clamp(26px, 3vw, 34px); - font-weight: 500; + font-size: clamp(21px, 3vw, 26px); + font-weight: 650; line-height: 1.25; } .sectionContent { min-width: 0; + margin-top: var(--mantine-spacing-md); } .document a { @@ -104,24 +107,27 @@ } @media (max-width: 48em) { - .main { - padding: 72px 0 88px; + .shell { + padding-top: 40px; + padding-bottom: 64px; } .intro { - padding-bottom: 48px; + padding-bottom: 36px; } .section { - grid-template-columns: 1fr; - gap: var(--mantine-spacing-md); - margin-top: 44px; - padding-top: 36px; + margin-top: 36px; + padding-top: 30px; } } @media (max-width: 36em) { + .shell { + padding-inline: var(--mantine-spacing-sm); + } + .main { - padding-top: 56px; + padding: 28px 22px; } } diff --git a/src/components/LegalPage/LegalPage.tsx b/src/components/LegalPage/LegalPage.tsx index 2b7da17..b5580c1 100644 --- a/src/components/LegalPage/LegalPage.tsx +++ b/src/components/LegalPage/LegalPage.tsx @@ -33,7 +33,7 @@ export function LegalPage({ - + {eyebrow} @@ -51,7 +51,7 @@ export function LegalPage({ {children} - + diff --git a/src/components/SiteFooter/SiteFooter.tsx b/src/components/SiteFooter/SiteFooter.tsx index b5f4ebe..8d489b7 100644 --- a/src/components/SiteFooter/SiteFooter.tsx +++ b/src/components/SiteFooter/SiteFooter.tsx @@ -33,6 +33,9 @@ export function SiteFooter() { Apps + + Encryption + Proto @@ -51,8 +54,8 @@ export function SiteFooter() { Terms of Use - - Admin@rosetta.im + + Support@rosetta.im diff --git a/src/components/SiteHeader/SiteHeader.module.css b/src/components/SiteHeader/SiteHeader.module.css index 47a7e03..393001a 100644 --- a/src/components/SiteHeader/SiteHeader.module.css +++ b/src/components/SiteHeader/SiteHeader.module.css @@ -30,13 +30,14 @@ color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1)); font-size: var(--mantine-font-size-sm); font-weight: 500; - text-decoration: none; + text-decoration: none !important; transition: color 120ms ease; } .link:hover, .link[data-active] { color: var(--mantine-color-blue-6); + text-decoration: none !important; } .burger { diff --git a/src/components/SiteHeader/SiteHeader.tsx b/src/components/SiteHeader/SiteHeader.tsx index e7a0c52..972d330 100644 --- a/src/components/SiteHeader/SiteHeader.tsx +++ b/src/components/SiteHeader/SiteHeader.tsx @@ -15,6 +15,7 @@ import classes from './SiteHeader.module.css'; const NAVIGATION = [ { label: 'Features', to: '/#features' }, { label: 'Apps', to: '/#apps' }, + { label: 'Encryption', to: '/encryption' }, { label: 'Proto', to: '/proto' }, { label: 'Support', to: '/support' }, ]; @@ -26,6 +27,7 @@ export function SiteHeader() { const links = NAVIGATION.map(({ label, to }) => { const active = (to === '/proto' && pathname === '/proto') || + (to === '/encryption' && pathname === '/encryption') || (to === '/support' && pathname === '/support'); return ( diff --git a/src/generated/protoDocs.ts b/src/generated/protoDocs.ts index 3991dc3..fec7b19 100644 --- a/src/generated/protoDocs.ts +++ b/src/generated/protoDocs.ts @@ -11,58 +11,60 @@ export const protoLayers = [ "name": "AttachmentType", "type": "int8", "description": "", + "yaml": "AttachmentType:\n type: int8\n values:\n - id: 0\n name: IMAGE\n - id: 1\n name: MESSAGES\n - id: 2\n name: FILE\n - id: 3\n name: AVATAR\n - id: 4\n name: CALL\n - id: 5\n name: VOICE\n - id: 6\n name: VIDEO_CIRCLE", "values": [ { "id": 0, "name": "IMAGE", - "description": "" + "description": "Image" }, { "id": 1, "name": "MESSAGES", - "description": "" + "description": "Reply/Forwarded messages" }, { "id": 2, "name": "FILE", - "description": "" + "description": "File" }, { "id": 3, "name": "AVATAR", - "description": "" + "description": "Avatar" }, { "id": 4, "name": "CALL", - "description": "" + "description": "Call" }, { "id": 5, "name": "VOICE", - "description": "" + "description": "Voice message" }, { "id": 6, "name": "VIDEO_CIRCLE", - "description": "" + "description": "Video circle" } ] }, { "name": "DeviceSolution", "type": "int8", - "description": "", + "description": "Solution by device after accept or decline request to attach new device", + "yaml": "DeviceSolution:\n type: int8\n values:\n - id: 0\n name: ACCEPT\n - id: 1\n name: DECLINE", "values": [ { "id": 0, "name": "ACCEPT", - "description": "" + "description": "Accept device to log in to account" }, { "id": 1, "name": "DECLINE", - "description": "" + "description": "Decline device" } ] }, @@ -70,6 +72,7 @@ export const protoLayers = [ "name": "HandshakeStage", "type": "int8", "description": "", + "yaml": "HandshakeStage:\n type: int8\n values:\n - id: 0\n name: COMPLETED\n - id: 1\n name: NEED_DEVICE_VERIFICATION", "values": [ { "id": 0, @@ -87,6 +90,7 @@ export const protoLayers = [ "name": "GroupStatus", "type": "int8", "description": "", + "yaml": "GroupStatus:\n type: int8\n values:\n - id: 0\n name: JOINED\n - id: 1\n name: INVALID\n - id: 2\n name: NOT_JOINED\n - id: 3\n name: BANNED", "values": [ { "id": 0, @@ -114,6 +118,7 @@ export const protoLayers = [ "name": "NotificationAction", "type": "int8", "description": "", + "yaml": "NotificationAction:\n type: int8\n values:\n - id: 0\n name: SUBSCRIBE\n - id: 1\n name: UNSUBSCRIBE", "values": [ { "id": 0, @@ -131,6 +136,7 @@ export const protoLayers = [ "name": "SignalType", "type": "int8", "description": "", + "yaml": "SignalType:\n type: int8\n values:\n - id: 0\n name: CALL\n - id: 1\n name: KEY_EXCHANGE\n - id: 2\n name: ACTIVE_CALL\n - id: 3\n name: END_CALL\n - id: 4\n name: ACTIVE\n - id: 5\n name: END_CALL_BECAUSE_PEER_DISCONNECTED\n - id: 6\n name: END_CALL_BECAUSE_BUSY\n - id: 7\n name: ACCEPT\n - id: 8\n name: RINGING_TIMEOUT", "values": [ { "id": 0, @@ -183,6 +189,7 @@ export const protoLayers = [ "name": "NetworkStatus", "type": "int8", "description": "", + "yaml": "NetworkStatus:\n type: int8\n values:\n - id: 0\n name: ONLINE\n - id: 1\n name: OFFLINE", "values": [ { "id": 0, @@ -200,6 +207,7 @@ export const protoLayers = [ "name": "SyncStatus", "type": "int8", "description": "", + "yaml": "SyncStatus:\n type: int8\n values:\n - id: 0\n name: NOT_NEEDED\n - id: 1\n name: BATCH_START\n - id: 2\n name: BATCH_END", "values": [ { "id": 0, @@ -222,6 +230,7 @@ export const protoLayers = [ "name": "WebRTCType", "type": "int8", "description": "", + "yaml": "WebRTCType:\n type: int8\n values:\n - id: 0\n name: OFFER\n - id: 1\n name: ANSWER\n - id: 2\n name: ICE_CANDIDATE", "values": [ { "id": 0, @@ -244,6 +253,7 @@ export const protoLayers = [ "name": "ResultCode", "type": "int8", "description": "", + "yaml": "ResultCode:\n type: int8\n values:\n - id: 0\n name: SUCCESS\n - id: 1\n name: ERROR\n - id: 2\n name: INVALID\n - id: 3\n name: USERNAME_TAKEN", "values": [ { "id": 0, @@ -271,6 +281,7 @@ export const protoLayers = [ "name": "TokenType", "type": "int8", "description": "", + "yaml": "TokenType:\n type: int8\n values:\n - id: 0\n name: FCM\n - id: 1\n name: VoIPApns", "values": [ { "id": 0, @@ -289,6 +300,7 @@ export const protoLayers = [ { "name": "Attachment", "description": "", + "yaml": "Attachment:\n fields:\n - id: 1\n name: id\n type: string\n - id: 2\n name: blob?\n type: string\n - id: 3\n name: type\n type: AttachmentType\n - id: 4\n name: preview\n type: string\n - id: 5\n name: transport\n type: AttachmentTransport", "fields": [ { "id": 1, @@ -330,6 +342,7 @@ export const protoLayers = [ { "name": "AttachmentTransport", "description": "", + "yaml": "AttachmentTransport:\n fields:\n - id: 1\n name: transportTag\n type: string\n - id: 2\n name: transportServer\n type: string", "fields": [ { "id": 1, @@ -350,6 +363,7 @@ export const protoLayers = [ { "name": "NetworkDevice", "description": "", + "yaml": "NetworkDevice:\n fields:\n - id: 1\n name: networkStatus\n type: NetworkStatus\n - id: 2\n name: deviceName\n type: string\n - id: 3\n name: deviceOs\n type: string\n - id: 4\n name: deviceId\n type: string\n - id: 5\n name: deviceSolution\n type: DeviceSolution", "fields": [ { "id": 1, @@ -391,6 +405,7 @@ export const protoLayers = [ { "name": "PKNetworkStatus", "description": "", + "yaml": "PKNetworkStatus:\n fields:\n - id: 1\n name: publicKey\n type: string\n - id: 2\n name: networkStatus\n type: NetworkStatus", "fields": [ { "id": 1, @@ -411,6 +426,7 @@ export const protoLayers = [ { "name": "SearchInfo", "description": "", + "yaml": "SearchInfo:\n fields:\n - id: 1\n name: username\n type: string\n - id: 2\n name: title\n type: string\n - id: 3\n name: publicKey\n type: string\n - id: 4\n name: verified\n type: int32\n - id: 5\n name: networkStatus\n type: NetworkStatus", "fields": [ { "id": 1, @@ -452,6 +468,7 @@ export const protoLayers = [ { "name": "RTCIceServer", "description": "", + "yaml": "RTCIceServer:\n fields:\n - id: 1\n name: url\n type: string\n - id: 2\n name: username\n type: string\n - id: 3\n name: credential\n type: string\n - id: 4\n name: transport\n type: string", "fields": [ { "id": 1, @@ -489,6 +506,7 @@ export const protoLayers = [ "name": "Handshake", "id": 0, "description": "", + "yaml": "Handshake:\n id: 0\n fields:\n - id: 1\n name: publicKey\n type: string\n - id: 2\n name: privateKey\n type: string\n - id: 3\n name: deviceId\n type: string\n - id: 4\n name: deviceName\n type: string\n - id: 5\n name: deviceOs\n type: string\n - id: 6\n name: handshakeStage?\n type: HandshakeStage", "fields": [ { "id": 1, @@ -538,6 +556,7 @@ export const protoLayers = [ "name": "UserInfo", "id": 1, "description": "", + "yaml": "UserInfo:\n id: 1\n fields:\n - id: 1\n name: privateKey\n type: string\n - id: 2\n name: username\n type: string\n - id: 3\n name: title\n type: string", "fields": [ { "id": 1, @@ -566,6 +585,7 @@ export const protoLayers = [ "name": "Result", "id": 2, "description": "", + "yaml": "Result:\n id: 2\n fields:\n - id: 1\n name: resultCode\n type: ResultCode", "fields": [ { "id": 1, @@ -580,6 +600,7 @@ export const protoLayers = [ "name": "Search", "id": 3, "description": "", + "yaml": "Search:\n id: 3\n fields:\n - id: 1\n name: privateKey\n type: string\n - id: 2\n name: search\n type: string\n - id: 3\n name: searchInfo?\n type: SearchInfo[]", "fields": [ { "id": 1, @@ -608,6 +629,7 @@ export const protoLayers = [ "name": "OnlineSubscribe", "id": 4, "description": "", + "yaml": "OnlineSubscribe:\n id: 4\n fields:\n - id: 1\n name: privateKey\n type: string\n - id: 2\n name: publicKeys\n type: string[]", "fields": [ { "id": 1, @@ -629,6 +651,7 @@ export const protoLayers = [ "name": "OnlineState", "id": 5, "description": "", + "yaml": "OnlineState:\n id: 5\n fields:\n - id: 1\n name: pkNetworkStatuses\n type: PKNetworkStatus[]", "fields": [ { "id": 1, @@ -643,6 +666,7 @@ export const protoLayers = [ "name": "Message", "id": 6, "description": "", + "yaml": "Message:\n id: 6\n fields:\n - id: 1\n name: fromPublicKey\n type: string\n - id: 2\n name: toPublicKey\n type: string\n - id: 3\n name: privateKey\n type: string\n - id: 4\n name: content\n type: string\n - id: 5\n name: chachaKey\n type: string\n - id: 6\n name: timestamp\n type: int64\n - id: 7\n name: messageId\n type: string\n - id: 8\n name: attachments?\n type: Attachment[]\n - id: 9\n name: aesChachaKey\n type: string", "fields": [ { "id": 1, @@ -713,6 +737,7 @@ export const protoLayers = [ "name": "Read", "id": 7, "description": "", + "yaml": "Read:\n id: 7\n fields:\n - id: 1\n name: fromPublicKey\n type: string\n - id: 2\n name: toPublicKey\n type: string\n - id: 3\n name: privateKey\n type: string", "fields": [ { "id": 1, @@ -741,6 +766,7 @@ export const protoLayers = [ "name": "Delivery", "id": 8, "description": "", + "yaml": "Delivery:\n id: 8\n fields:\n - id: 1\n name: messageId\n type: string\n - id: 2\n name: toPublicKey\n type: string", "fields": [ { "id": 1, @@ -762,6 +788,7 @@ export const protoLayers = [ "name": "DeviceNew", "id": 9, "description": "", + "yaml": "DeviceNew:\n id: 9\n fields:\n - id: 1\n name: ipAddress\n type: string\n - id: 2\n name: deviceId\n type: string\n - id: 3\n name: deviceName\n type: string\n - id: 4\n name: deviceOs\n type: string", "fields": [ { "id": 1, @@ -797,6 +824,7 @@ export const protoLayers = [ "name": "RequestUpdate", "id": 10, "description": "", + "yaml": "RequestUpdate:\n id: 10\n fields:\n - id: 1\n name: server?\n type: string", "fields": [ { "id": 1, @@ -811,6 +839,7 @@ export const protoLayers = [ "name": "Typeing", "id": 11, "description": "", + "yaml": "Typeing:\n id: 11\n fields:\n - id: 1\n name: fromPublicKey\n type: string\n - id: 2\n name: toPublicKey\n type: string\n - id: 3\n name: privateKey\n type: string", "fields": [ { "id": 1, @@ -839,6 +868,7 @@ export const protoLayers = [ "name": "RequestTransport", "id": 15, "description": "", + "yaml": "RequestTransport:\n id: 15\n fields:\n - id: 1\n name: server?\n type: string", "fields": [ { "id": 1, @@ -853,6 +883,7 @@ export const protoLayers = [ "name": "PushNotification", "id": 16, "description": "", + "yaml": "PushNotification:\n id: 16\n fields:\n - id: 1\n name: notificationToken\n type: string\n - id: 2\n name: action\n type: NotificationAction\n - id: 3\n name: tokenType\n type: TokenType\n - id: 4\n name: deviceId\n type: string", "fields": [ { "id": 1, @@ -888,6 +919,7 @@ export const protoLayers = [ "name": "GroupCreate", "id": 17, "description": "", + "yaml": "GroupCreate:\n id: 17\n fields:\n - id: 1\n name: groupId\n type: string", "fields": [ { "id": 1, @@ -902,6 +934,7 @@ export const protoLayers = [ "name": "GroupInfo", "id": 18, "description": "", + "yaml": "GroupInfo:\n id: 18\n fields:\n - id: 1\n name: groupId\n type: string\n - id: 2\n name: membersPKs?\n type: string[]", "fields": [ { "id": 1, @@ -923,6 +956,7 @@ export const protoLayers = [ "name": "GroupInviteInfo", "id": 19, "description": "", + "yaml": "GroupInviteInfo:\n id: 19\n fields:\n - id: 1\n name: groupId\n type: string\n - id: 2\n name: membersCount?\n type: int32\n - id: 3\n name: status?\n type: GroupStatus", "fields": [ { "id": 1, @@ -951,6 +985,7 @@ export const protoLayers = [ "name": "GroupJoin", "id": 20, "description": "", + "yaml": "GroupJoin:\n id: 20\n fields:\n - id: 1\n name: groupId\n type: string\n - id: 2\n name: status?\n type: GroupStatus\n - id: 3\n name: groupString\n type: string", "fields": [ { "id": 1, @@ -979,6 +1014,7 @@ export const protoLayers = [ "name": "GroupLeave", "id": 21, "description": "", + "yaml": "GroupLeave:\n id: 21\n fields:\n - id: 1\n name: groupId\n type: string", "fields": [ { "id": 1, @@ -993,6 +1029,7 @@ export const protoLayers = [ "name": "GroupBan", "id": 22, "description": "", + "yaml": "GroupBan:\n id: 22\n fields:\n - id: 1\n name: groupId\n type: string\n - id: 2\n name: publicKey\n type: string", "fields": [ { "id": 1, @@ -1014,6 +1051,7 @@ export const protoLayers = [ "name": "DeviceList", "id": 23, "description": "", + "yaml": "DeviceList:\n id: 23\n fields:\n - id: 1\n name: devices\n type: NetworkDevice[]", "fields": [ { "id": 1, @@ -1028,6 +1066,7 @@ export const protoLayers = [ "name": "DeviceResolve", "id": 24, "description": "", + "yaml": "DeviceResolve:\n id: 24\n fields:\n - id: 1\n name: deviceId\n type: string\n - id: 2\n name: solution\n type: DeviceSolution", "fields": [ { "id": 1, @@ -1049,6 +1088,7 @@ export const protoLayers = [ "name": "Sync", "id": 25, "description": "", + "yaml": "Sync:\n id: 25\n fields:\n - id: 1\n name: syncStatus\n type: SyncStatus\n - id: 2\n name: timestamp\n type: int64", "fields": [ { "id": 1, @@ -1070,6 +1110,7 @@ export const protoLayers = [ "name": "SignalPeer", "id": 26, "description": "", + "yaml": "SignalPeer:\n id: 26\n fields:\n - id: 1\n name: src\n type: string\n - id: 2\n name: dst\n type: string\n - id: 3\n name: sharedPublic\n type: string\n - id: 4\n name: signalType\n type: SignalType\n - id: 5\n name: callId\n type: string\n - id: 6\n name: joinToken\n type: string", "fields": [ { "id": 1, @@ -1119,6 +1160,7 @@ export const protoLayers = [ "name": "WebRTC", "id": 27, "description": "", + "yaml": "WebRTC:\n id: 27\n fields:\n - id: 1\n name: sdpOrCandidate\n type: string\n - id: 2\n name: type\n type: WebRTCType", "fields": [ { "id": 1, @@ -1140,6 +1182,7 @@ export const protoLayers = [ "name": "IceServers", "id": 28, "description": "", + "yaml": "IceServers:\n id: 28\n fields:\n - id: 1\n name: iceServers?\n type: RTCIceServer[]", "fields": [ { "id": 1, @@ -1161,6 +1204,7 @@ export const protoLayers = [ "name": "AttachmentType", "type": "int8", "description": "", + "yaml": "AttachmentType:\n type: int8\n values:\n - id: 0\n name: IMAGE\n - id: 1\n name: MESSAGES\n - id: 2\n name: FILE\n - id: 4\n name: CALL\n - id: 5\n name: VOICE\n - id: 6\n name: VIDEO_CIRCLE\n - id: 7\n name: VIDEO", "values": [ { "id": 0, @@ -1203,6 +1247,7 @@ export const protoLayers = [ "name": "DeviceSolution", "type": "int8", "description": "", + "yaml": "DeviceSolution:\n type: int8\n values:\n - id: 0\n name: ACCEPT\n - id: 1\n name: DECLINE", "values": [ { "id": 0, @@ -1220,6 +1265,7 @@ export const protoLayers = [ "name": "HandshakeStage", "type": "int8", "description": "", + "yaml": "HandshakeStage:\n type: int8\n values:\n - id: 0\n name: COMPLETED\n - id: 1\n name: NEED_DEVICE_VERIFICATION", "values": [ { "id": 0, @@ -1237,6 +1283,7 @@ export const protoLayers = [ "name": "GroupStatus", "type": "int8", "description": "", + "yaml": "GroupStatus:\n type: int8\n values:\n - id: 0\n name: JOINED\n - id: 1\n name: INVALID\n - id: 2\n name: NOT_JOINED\n - id: 3\n name: BANNED", "values": [ { "id": 0, @@ -1264,6 +1311,7 @@ export const protoLayers = [ "name": "NotificationAction", "type": "int8", "description": "", + "yaml": "NotificationAction:\n type: int8\n values:\n - id: 0\n name: SUBSCRIBE\n - id: 1\n name: UNSUBSCRIBE", "values": [ { "id": 0, @@ -1281,6 +1329,7 @@ export const protoLayers = [ "name": "SignalType", "type": "int8", "description": "", + "yaml": "SignalType:\n type: int8\n values:\n - id: 0\n name: CALL\n - id: 1\n name: KEY_EXCHANGE\n - id: 2\n name: ACTIVE_CALL\n - id: 3\n name: END_CALL\n - id: 4\n name: ACTIVE\n - id: 5\n name: END_CALL_BECAUSE_PEER_DISCONNECTED\n - id: 6\n name: END_CALL_BECAUSE_BUSY\n - id: 7\n name: ACCEPT\n - id: 8\n name: RINGING_TIMEOUT", "values": [ { "id": 0, @@ -1333,6 +1382,7 @@ export const protoLayers = [ "name": "NetworkStatus", "type": "int8", "description": "", + "yaml": "NetworkStatus:\n type: int8\n values:\n - id: 0\n name: ONLINE\n - id: 1\n name: OFFLINE", "values": [ { "id": 0, @@ -1350,6 +1400,7 @@ export const protoLayers = [ "name": "SyncStatus", "type": "int8", "description": "", + "yaml": "SyncStatus:\n type: int8\n values:\n - id: 0\n name: NOT_NEEDED\n - id: 1\n name: BATCH_START\n - id: 2\n name: BATCH_END", "values": [ { "id": 0, @@ -1372,6 +1423,7 @@ export const protoLayers = [ "name": "WebRTCType", "type": "int8", "description": "", + "yaml": "WebRTCType:\n type: int8\n values:\n - id: 0\n name: OFFER\n - id: 1\n name: ANSWER\n - id: 2\n name: ICE_CANDIDATE", "values": [ { "id": 0, @@ -1394,6 +1446,7 @@ export const protoLayers = [ "name": "ResultCode", "type": "int8", "description": "", + "yaml": "ResultCode:\n type: int8\n values:\n - id: 0\n name: SUCCESS\n - id: 1\n name: ERROR\n - id: 2\n name: INVALID\n - id: 3\n name: USERNAME_TAKEN", "values": [ { "id": 0, @@ -1421,6 +1474,7 @@ export const protoLayers = [ "name": "TokenType", "type": "int8", "description": "", + "yaml": "TokenType:\n type: int8\n values:\n - id: 0\n name: FCM\n - id: 1\n name: VoIPApns", "values": [ { "id": 0, @@ -1439,6 +1493,7 @@ export const protoLayers = [ { "name": "Attachment", "description": "", + "yaml": "Attachment:\n fields:\n - id: 1\n name: id\n type: string\n - id: 2\n name: blob?\n type: string\n - id: 3\n name: type\n type: AttachmentType\n - id: 4\n name: preview\n type: string\n - id: 5\n name: transport\n type: AttachmentTransport", "fields": [ { "id": 1, @@ -1480,6 +1535,7 @@ export const protoLayers = [ { "name": "AttachmentTransport", "description": "", + "yaml": "AttachmentTransport:\n fields:\n - id: 1\n name: transportTag\n type: string\n - id: 2\n name: transportServer\n type: string", "fields": [ { "id": 1, @@ -1500,6 +1556,7 @@ export const protoLayers = [ { "name": "NetworkDevice", "description": "", + "yaml": "NetworkDevice:\n fields:\n - id: 1\n name: networkStatus\n type: NetworkStatus\n - id: 2\n name: deviceName\n type: string\n - id: 3\n name: deviceOs\n type: string\n - id: 4\n name: deviceId\n type: string\n - id: 5\n name: deviceSolution\n type: DeviceSolution", "fields": [ { "id": 1, @@ -1541,6 +1598,7 @@ export const protoLayers = [ { "name": "PKNetworkStatus", "description": "", + "yaml": "PKNetworkStatus:\n fields:\n - id: 1\n name: publicKey\n type: string\n - id: 2\n name: networkStatus\n type: NetworkStatus", "fields": [ { "id": 1, @@ -1561,6 +1619,7 @@ export const protoLayers = [ { "name": "SearchInfo", "description": "", + "yaml": "SearchInfo:\n fields:\n - id: 1\n name: username\n type: string\n - id: 2\n name: title\n type: string\n - id: 3\n name: publicKey\n type: string\n - id: 4\n name: verified\n type: int32\n - id: 5\n name: networkStatus\n type: NetworkStatus", "fields": [ { "id": 1, @@ -1602,6 +1661,7 @@ export const protoLayers = [ { "name": "RTCIceServer", "description": "", + "yaml": "RTCIceServer:\n fields:\n - id: 1\n name: url\n type: string\n - id: 2\n name: username\n type: string\n - id: 3\n name: credential\n type: string\n - id: 4\n name: transport\n type: string", "fields": [ { "id": 1, @@ -1639,6 +1699,7 @@ export const protoLayers = [ "name": "Handshake", "id": 0, "description": "", + "yaml": "Handshake:\n id: 0\n fields:\n - id: 1\n name: publicKey\n type: string\n - id: 2\n name: privateKey\n type: string\n - id: 3\n name: deviceId\n type: string\n - id: 4\n name: deviceName\n type: string\n - id: 5\n name: deviceOs\n type: string\n - id: 6\n name: handshakeStage?\n type: HandshakeStage", "fields": [ { "id": 1, @@ -1688,6 +1749,7 @@ export const protoLayers = [ "name": "UserInfo", "id": 1, "description": "", + "yaml": "UserInfo:\n id: 1\n fields:\n - id: 1\n name: privateKey\n type: string\n - id: 2\n name: username\n type: string\n - id: 3\n name: title\n type: string", "fields": [ { "id": 1, @@ -1716,6 +1778,7 @@ export const protoLayers = [ "name": "Result", "id": 2, "description": "", + "yaml": "Result:\n id: 2\n fields:\n - id: 1\n name: resultCode\n type: ResultCode", "fields": [ { "id": 1, @@ -1730,6 +1793,7 @@ export const protoLayers = [ "name": "Search", "id": 3, "description": "", + "yaml": "Search:\n id: 3\n fields:\n - id: 1\n name: privateKey\n type: string\n - id: 2\n name: search\n type: string\n - id: 3\n name: searchInfo?\n type: SearchInfo[]", "fields": [ { "id": 1, @@ -1758,6 +1822,7 @@ export const protoLayers = [ "name": "OnlineSubscribe", "id": 4, "description": "", + "yaml": "OnlineSubscribe:\n id: 4\n fields:\n - id: 1\n name: privateKey\n type: string\n - id: 2\n name: publicKeys\n type: string[]", "fields": [ { "id": 1, @@ -1779,6 +1844,7 @@ export const protoLayers = [ "name": "OnlineState", "id": 5, "description": "", + "yaml": "OnlineState:\n id: 5\n fields:\n - id: 1\n name: pkNetworkStatuses\n type: PKNetworkStatus[]", "fields": [ { "id": 1, @@ -1793,6 +1859,7 @@ export const protoLayers = [ "name": "Message", "id": 6, "description": "", + "yaml": "Message:\n id: 6\n fields:\n - id: 1\n name: fromPublicKey\n type: string\n - id: 2\n name: toPublicKey\n type: string\n - id: 3\n name: privateKey\n type: string\n - id: 4\n name: content\n type: string\n - id: 5\n name: chachaKey\n type: string\n - id: 6\n name: timestamp\n type: int64\n - id: 7\n name: messageId\n type: string\n - id: 8\n name: attachments?\n type: Attachment[]\n - id: 9\n name: aesChachaKey\n type: string", "fields": [ { "id": 1, @@ -1863,6 +1930,7 @@ export const protoLayers = [ "name": "Read", "id": 7, "description": "", + "yaml": "Read:\n id: 7\n fields:\n - id: 1\n name: fromPublicKey\n type: string\n - id: 2\n name: toPublicKey\n type: string\n - id: 4\n name: timestamp\n type: int64", "fields": [ { "id": 1, @@ -1891,6 +1959,7 @@ export const protoLayers = [ "name": "Delivery", "id": 8, "description": "", + "yaml": "Delivery:\n id: 8\n fields:\n - id: 1\n name: messageId\n type: string\n - id: 2\n name: toPublicKey\n type: string", "fields": [ { "id": 1, @@ -1912,6 +1981,7 @@ export const protoLayers = [ "name": "DeviceNew", "id": 9, "description": "", + "yaml": "DeviceNew:\n id: 9\n fields:\n - id: 1\n name: ipAddress\n type: string\n - id: 2\n name: deviceId\n type: string\n - id: 3\n name: deviceName\n type: string\n - id: 4\n name: deviceOs\n type: string", "fields": [ { "id": 1, @@ -1947,6 +2017,7 @@ export const protoLayers = [ "name": "RequestUpdate", "id": 10, "description": "", + "yaml": "RequestUpdate:\n id: 10\n fields:\n - id: 1\n name: server?\n type: string", "fields": [ { "id": 1, @@ -1961,6 +2032,7 @@ export const protoLayers = [ "name": "Typeing", "id": 11, "description": "", + "yaml": "Typeing:\n id: 11\n fields:\n - id: 1\n name: fromPublicKey\n type: string\n - id: 2\n name: toPublicKey\n type: string\n - id: 3\n name: privateKey\n type: string", "fields": [ { "id": 1, @@ -1989,6 +2061,7 @@ export const protoLayers = [ "name": "RequestTransport", "id": 15, "description": "", + "yaml": "RequestTransport:\n id: 15\n fields:\n - id: 1\n name: server?\n type: string", "fields": [ { "id": 1, @@ -2003,6 +2076,7 @@ export const protoLayers = [ "name": "PushNotification", "id": 16, "description": "", + "yaml": "PushNotification:\n id: 16\n fields:\n - id: 1\n name: notificationToken\n type: string\n - id: 2\n name: action\n type: NotificationAction\n - id: 3\n name: tokenType\n type: TokenType\n - id: 4\n name: deviceId\n type: string", "fields": [ { "id": 1, @@ -2038,6 +2112,7 @@ export const protoLayers = [ "name": "GroupCreate", "id": 17, "description": "", + "yaml": "GroupCreate:\n id: 17\n fields:\n - id: 1\n name: groupId?\n type: string", "fields": [ { "id": 1, @@ -2052,6 +2127,7 @@ export const protoLayers = [ "name": "GroupInfo", "id": 18, "description": "", + "yaml": "GroupInfo:\n id: 18\n fields:\n - id: 1\n name: groupId\n type: string\n - id: 2\n name: membersPKs?\n type: string[]", "fields": [ { "id": 1, @@ -2073,6 +2149,7 @@ export const protoLayers = [ "name": "GroupInviteInfo", "id": 19, "description": "", + "yaml": "GroupInviteInfo:\n id: 19\n fields:\n - id: 1\n name: groupId\n type: string\n - id: 2\n name: membersCount?\n type: int32\n - id: 3\n name: status?\n type: GroupStatus", "fields": [ { "id": 1, @@ -2101,6 +2178,7 @@ export const protoLayers = [ "name": "GroupJoin", "id": 20, "description": "", + "yaml": "GroupJoin:\n id: 20\n fields:\n - id: 1\n name: groupId\n type: string\n - id: 2\n name: status?\n type: GroupStatus\n - id: 3\n name: groupString\n type: string", "fields": [ { "id": 1, @@ -2129,6 +2207,7 @@ export const protoLayers = [ "name": "GroupLeave", "id": 21, "description": "", + "yaml": "GroupLeave:\n id: 21\n fields:\n - id: 1\n name: groupId\n type: string", "fields": [ { "id": 1, @@ -2143,6 +2222,7 @@ export const protoLayers = [ "name": "GroupBan", "id": 22, "description": "", + "yaml": "GroupBan:\n id: 22\n fields:\n - id: 1\n name: groupId\n type: string\n - id: 2\n name: publicKey\n type: string", "fields": [ { "id": 1, @@ -2164,6 +2244,7 @@ export const protoLayers = [ "name": "DeviceList", "id": 23, "description": "", + "yaml": "DeviceList:\n id: 23\n fields:\n - id: 1\n name: devices\n type: NetworkDevice[]", "fields": [ { "id": 1, @@ -2178,6 +2259,7 @@ export const protoLayers = [ "name": "DeviceResolve", "id": 24, "description": "", + "yaml": "DeviceResolve:\n id: 24\n fields:\n - id: 1\n name: deviceId\n type: string\n - id: 2\n name: solution\n type: DeviceSolution", "fields": [ { "id": 1, @@ -2199,6 +2281,7 @@ export const protoLayers = [ "name": "Sync", "id": 25, "description": "", + "yaml": "Sync:\n id: 25\n fields:\n - id: 1\n name: syncStatus\n type: SyncStatus\n - id: 2\n name: timestamp\n type: int64", "fields": [ { "id": 1, @@ -2220,6 +2303,7 @@ export const protoLayers = [ "name": "SignalPeer", "id": 26, "description": "", + "yaml": "SignalPeer:\n id: 26\n fields:\n - id: 1\n name: src\n type: string\n - id: 2\n name: dst\n type: string\n - id: 3\n name: sharedPublic?\n type: string\n - id: 4\n name: signalType\n type: SignalType\n - id: 5\n name: callId?\n type: string\n - id: 6\n name: joinToken?\n type: string", "fields": [ { "id": 1, @@ -2269,6 +2353,7 @@ export const protoLayers = [ "name": "WebRTC", "id": 27, "description": "", + "yaml": "WebRTC:\n id: 27\n fields:\n - id: 1\n name: sdpOrCandidate\n type: string\n - id: 2\n name: type\n type: WebRTCType", "fields": [ { "id": 1, @@ -2290,6 +2375,7 @@ export const protoLayers = [ "name": "IceServers", "id": 28, "description": "", + "yaml": "IceServers:\n id: 28\n fields:\n - id: 1\n name: iceServers?\n type: RTCIceServer[]", "fields": [ { "id": 1, @@ -2311,6 +2397,7 @@ export const protoLayers = [ "name": "AttachmentType", "type": "int8", "description": "", + "yaml": "AttachmentType:\n type: int8\n values:\n - id: 0\n name: IMAGE\n - id: 1\n name: MESSAGES\n - id: 2\n name: FILE\n - id: 4\n name: CALL\n - id: 5\n name: VOICE\n - id: 6\n name: VIDEO_CIRCLE\n - id: 7\n name: VIDEO", "values": [ { "id": 0, @@ -2353,6 +2440,7 @@ export const protoLayers = [ "name": "DeviceSolution", "type": "int8", "description": "", + "yaml": "DeviceSolution:\n type: int8\n values:\n - id: 0\n name: ACCEPT\n - id: 1\n name: DECLINE", "values": [ { "id": 0, @@ -2370,6 +2458,7 @@ export const protoLayers = [ "name": "HandshakeStage", "type": "int8", "description": "", + "yaml": "HandshakeStage:\n type: int8\n values:\n - id: 0\n name: COMPLETED\n - id: 1\n name: NEED_DEVICE_VERIFICATION", "values": [ { "id": 0, @@ -2387,6 +2476,7 @@ export const protoLayers = [ "name": "GroupStatus", "type": "int8", "description": "", + "yaml": "GroupStatus:\n type: int8\n values:\n - id: 0\n name: JOINED\n - id: 1\n name: INVALID\n - id: 2\n name: NOT_JOINED\n - id: 3\n name: BANNED", "values": [ { "id": 0, @@ -2414,6 +2504,7 @@ export const protoLayers = [ "name": "NotificationAction", "type": "int8", "description": "", + "yaml": "NotificationAction:\n type: int8\n values:\n - id: 0\n name: SUBSCRIBE\n - id: 1\n name: UNSUBSCRIBE", "values": [ { "id": 0, @@ -2431,6 +2522,7 @@ export const protoLayers = [ "name": "SignalType", "type": "int8", "description": "", + "yaml": "SignalType:\n type: int8\n values:\n - id: 0\n name: CALL\n - id: 1\n name: KEY_EXCHANGE\n - id: 2\n name: ACTIVE_CALL\n - id: 3\n name: END_CALL\n - id: 4\n name: ACTIVE\n - id: 5\n name: END_CALL_BECAUSE_PEER_DISCONNECTED\n - id: 6\n name: END_CALL_BECAUSE_BUSY\n - id: 7\n name: ACCEPT\n - id: 8\n name: RINGING_TIMEOUT", "values": [ { "id": 0, @@ -2483,6 +2575,7 @@ export const protoLayers = [ "name": "NetworkStatus", "type": "int8", "description": "", + "yaml": "NetworkStatus:\n type: int8\n values:\n - id: 0\n name: ONLINE\n - id: 1\n name: OFFLINE", "values": [ { "id": 0, @@ -2500,6 +2593,7 @@ export const protoLayers = [ "name": "SyncStatus", "type": "int8", "description": "", + "yaml": "SyncStatus:\n type: int8\n values:\n - id: 0\n name: NOT_NEEDED\n - id: 1\n name: BATCH_START\n - id: 2\n name: BATCH_END", "values": [ { "id": 0, @@ -2522,6 +2616,7 @@ export const protoLayers = [ "name": "WebRTCType", "type": "int8", "description": "", + "yaml": "WebRTCType:\n type: int8\n values:\n - id: 0\n name: OFFER\n - id: 1\n name: ANSWER\n - id: 2\n name: ICE_CANDIDATE", "values": [ { "id": 0, @@ -2544,6 +2639,7 @@ export const protoLayers = [ "name": "ResultCode", "type": "int8", "description": "", + "yaml": "ResultCode:\n type: int8\n values:\n - id: 0\n name: SUCCESS\n - id: 1\n name: ERROR\n - id: 2\n name: INVALID\n - id: 3\n name: USERNAME_TAKEN\n - id: 4\n name: OVERSIZE\n - id: 5\n name: OVERLIFE", "values": [ { "id": 0, @@ -2581,6 +2677,7 @@ export const protoLayers = [ "name": "TokenType", "type": "int8", "description": "", + "yaml": "TokenType:\n type: int8\n values:\n - id: 0\n name: FCM\n - id: 1\n name: VoIPApns", "values": [ { "id": 0, @@ -2598,6 +2695,7 @@ export const protoLayers = [ "name": "ProfileMediaType", "type": "uint8", "description": "Тип медиа файла профиля (пока только фото)", + "yaml": "ProfileMediaType:\n type: uint8\n values:\n - id: 0\n name: PROFILE_PHOTO", "values": [ { "id": 0, @@ -2611,6 +2709,7 @@ export const protoLayers = [ { "name": "Attachment", "description": "", + "yaml": "Attachment:\n fields:\n - id: 1\n name: id\n type: string\n - id: 2\n name: blob?\n type: string\n - id: 3\n name: type\n type: AttachmentType\n - id: 4\n name: preview\n type: string\n - id: 6\n name: objectId\n type: string", "fields": [ { "id": 1, @@ -2652,6 +2751,7 @@ export const protoLayers = [ { "name": "ProfileMedia", "description": "Медиа-файлы профиля такие как аватарки (фото) и будущие истории", + "yaml": "ProfileMedia:\n fields:\n - id: 1\n name: preview\n type: string\n - id: 2\n name: objectId\n type: string\n - id: 3\n name: type\n type: ProfileMediaType\n - id: 4\n name: timestamp\n type: int64\n - id: 5\n name: mediaKey\n type: string", "fields": [ { "id": 1, @@ -2693,6 +2793,7 @@ export const protoLayers = [ { "name": "NetworkDevice", "description": "", + "yaml": "NetworkDevice:\n fields:\n - id: 1\n name: networkStatus\n type: NetworkStatus\n - id: 2\n name: deviceName\n type: string\n - id: 3\n name: deviceOs\n type: string\n - id: 4\n name: deviceId\n type: string\n - id: 5\n name: deviceSolution\n type: DeviceSolution", "fields": [ { "id": 1, @@ -2734,6 +2835,7 @@ export const protoLayers = [ { "name": "PKNetworkStatus", "description": "", + "yaml": "PKNetworkStatus:\n fields:\n - id: 1\n name: publicKey\n type: string\n - id: 2\n name: networkStatus\n type: NetworkStatus", "fields": [ { "id": 1, @@ -2754,6 +2856,7 @@ export const protoLayers = [ { "name": "SearchInfo", "description": "Информация при поиске пользователя (приходит в ответ на пакет PacketSearch)", + "yaml": "SearchInfo:\n fields:\n - id: 1\n name: username\n type: string\n - id: 2\n name: title\n type: string\n - id: 3\n name: publicKey\n type: string\n - id: 4\n name: verified\n type: int32\n - id: 5\n name: networkStatus\n type: NetworkStatus\n - id: 6\n name: avatar?\n type: ProfileMedia", "fields": [ { "id": 1, @@ -2802,6 +2905,7 @@ export const protoLayers = [ { "name": "ProfileInfo", "description": "Информация которая используется только при входе в профиль пользователя", + "yaml": "ProfileInfo:\n fields:\n - id: 1\n name: username\n type: string\n - id: 2\n name: title\n type: string\n - id: 3\n name: publicKey\n type: string\n - id: 4\n name: verified\n type: int32\n - id: 5\n name: networkStatus\n type: NetworkStatus\n - id: 6\n name: media?\n type: ProfileMedia[]", "fields": [ { "id": 1, @@ -2850,6 +2954,7 @@ export const protoLayers = [ { "name": "RTCIceServer", "description": "", + "yaml": "RTCIceServer:\n fields:\n - id: 1\n name: url\n type: string\n - id: 2\n name: username\n type: string\n - id: 3\n name: credential\n type: string\n - id: 4\n name: transport\n type: string", "fields": [ { "id": 1, @@ -2887,6 +2992,7 @@ export const protoLayers = [ "name": "Handshake", "id": 0, "description": "", + "yaml": "Handshake:\n id: 0\n fields:\n - id: 1\n name: publicKey\n type: string\n - id: 2\n name: privateKey\n type: string\n - id: 3\n name: deviceId\n type: string\n - id: 4\n name: deviceName\n type: string\n - id: 5\n name: deviceOs\n type: string\n - id: 6\n name: handshakeStage?\n type: HandshakeStage", "fields": [ { "id": 1, @@ -2936,6 +3042,7 @@ export const protoLayers = [ "name": "UserInfo", "id": 1, "description": "", + "yaml": "UserInfo:\n id: 1\n fields:\n - id: 1\n name: privateKey\n type: string\n - id: 2\n name: username\n type: string\n - id: 3\n name: title\n type: string", "fields": [ { "id": 1, @@ -2964,6 +3071,7 @@ export const protoLayers = [ "name": "Result", "id": 2, "description": "", + "yaml": "Result:\n id: 2\n fields:\n - id: 1\n name: resultCode\n type: ResultCode", "fields": [ { "id": 1, @@ -2978,6 +3086,7 @@ export const protoLayers = [ "name": "Search", "id": 3, "description": "", + "yaml": "Search:\n id: 3\n fields:\n - id: 1\n name: privateKey\n type: string\n - id: 2\n name: search\n type: string\n - id: 3\n name: searchInfo?\n type: SearchInfo[]", "fields": [ { "id": 1, @@ -3006,6 +3115,7 @@ export const protoLayers = [ "name": "OnlineSubscribe", "id": 4, "description": "", + "yaml": "OnlineSubscribe:\n id: 4\n fields:\n - id: 1\n name: privateKey\n type: string\n - id: 2\n name: publicKeys\n type: string[]", "fields": [ { "id": 1, @@ -3027,6 +3137,7 @@ export const protoLayers = [ "name": "OnlineState", "id": 5, "description": "", + "yaml": "OnlineState:\n id: 5\n fields:\n - id: 1\n name: pkNetworkStatuses\n type: PKNetworkStatus[]", "fields": [ { "id": 1, @@ -3041,6 +3152,7 @@ export const protoLayers = [ "name": "Message", "id": 6, "description": "", + "yaml": "Message:\n id: 6\n fields:\n - id: 1\n name: fromPublicKey\n type: string\n - id: 2\n name: toPublicKey\n type: string\n - id: 3\n name: privateKey\n type: string\n - id: 4\n name: content\n type: string\n - id: 5\n name: chachaKey\n type: string\n - id: 6\n name: timestamp\n type: int64\n - id: 7\n name: messageId\n type: string\n - id: 8\n name: attachments?\n type: Attachment[]\n - id: 9\n name: aesChachaKey\n type: string", "fields": [ { "id": 1, @@ -3111,6 +3223,7 @@ export const protoLayers = [ "name": "Read", "id": 7, "description": "", + "yaml": "Read:\n id: 7\n fields:\n - id: 1\n name: fromPublicKey\n type: string\n - id: 2\n name: toPublicKey\n type: string\n - id: 4\n name: timestamp\n type: int64", "fields": [ { "id": 1, @@ -3139,6 +3252,7 @@ export const protoLayers = [ "name": "Delivery", "id": 8, "description": "", + "yaml": "Delivery:\n id: 8\n fields:\n - id: 1\n name: messageId\n type: string\n - id: 2\n name: toPublicKey\n type: string", "fields": [ { "id": 1, @@ -3160,6 +3274,7 @@ export const protoLayers = [ "name": "DeviceNew", "id": 9, "description": "", + "yaml": "DeviceNew:\n id: 9\n fields:\n - id: 1\n name: ipAddress\n type: string\n - id: 2\n name: deviceId\n type: string\n - id: 3\n name: deviceName\n type: string\n - id: 4\n name: deviceOs\n type: string", "fields": [ { "id": 1, @@ -3195,6 +3310,7 @@ export const protoLayers = [ "name": "RequestUpdate", "id": 10, "description": "", + "yaml": "RequestUpdate:\n id: 10\n fields:\n - id: 1\n name: server?\n type: string", "fields": [ { "id": 1, @@ -3209,6 +3325,7 @@ export const protoLayers = [ "name": "Typeing", "id": 11, "description": "", + "yaml": "Typeing:\n id: 11\n fields:\n - id: 1\n name: fromPublicKey\n type: string\n - id: 2\n name: toPublicKey\n type: string\n - id: 3\n name: privateKey\n type: string", "fields": [ { "id": 1, @@ -3237,6 +3354,7 @@ export const protoLayers = [ "name": "RequestTransport", "id": 15, "description": "Запрос транспортного сервера", + "yaml": "RequestTransport:\n id: 15\n fields:\n - id: 1\n name: server?\n type: string", "fields": [ { "id": 1, @@ -3251,6 +3369,7 @@ export const protoLayers = [ "name": "PushNotification", "id": 16, "description": "", + "yaml": "PushNotification:\n id: 16\n fields:\n - id: 1\n name: notificationToken\n type: string\n - id: 2\n name: action\n type: NotificationAction\n - id: 3\n name: tokenType\n type: TokenType\n - id: 4\n name: deviceId\n type: string", "fields": [ { "id": 1, @@ -3286,6 +3405,7 @@ export const protoLayers = [ "name": "GroupCreate", "id": 17, "description": "", + "yaml": "GroupCreate:\n id: 17\n fields:\n - id: 1\n name: groupId?\n type: string", "fields": [ { "id": 1, @@ -3300,6 +3420,7 @@ export const protoLayers = [ "name": "GroupInfo", "id": 18, "description": "", + "yaml": "GroupInfo:\n id: 18\n fields:\n - id: 1\n name: groupId\n type: string\n - id: 2\n name: membersPKs?\n type: string[]", "fields": [ { "id": 1, @@ -3321,6 +3442,7 @@ export const protoLayers = [ "name": "GroupInviteInfo", "id": 19, "description": "", + "yaml": "GroupInviteInfo:\n id: 19\n fields:\n - id: 1\n name: groupId\n type: string\n - id: 2\n name: membersCount?\n type: int32\n - id: 3\n name: status?\n type: GroupStatus", "fields": [ { "id": 1, @@ -3349,6 +3471,7 @@ export const protoLayers = [ "name": "GroupJoin", "id": 20, "description": "", + "yaml": "GroupJoin:\n id: 20\n fields:\n - id: 1\n name: groupId\n type: string\n - id: 2\n name: status?\n type: GroupStatus\n - id: 3\n name: groupString\n type: string", "fields": [ { "id": 1, @@ -3377,6 +3500,7 @@ export const protoLayers = [ "name": "GroupLeave", "id": 21, "description": "", + "yaml": "GroupLeave:\n id: 21\n fields:\n - id: 1\n name: groupId\n type: string", "fields": [ { "id": 1, @@ -3391,6 +3515,7 @@ export const protoLayers = [ "name": "GroupBan", "id": 22, "description": "", + "yaml": "GroupBan:\n id: 22\n fields:\n - id: 1\n name: groupId\n type: string\n - id: 2\n name: publicKey\n type: string", "fields": [ { "id": 1, @@ -3412,6 +3537,7 @@ export const protoLayers = [ "name": "DeviceList", "id": 23, "description": "", + "yaml": "DeviceList:\n id: 23\n fields:\n - id: 1\n name: devices\n type: NetworkDevice[]", "fields": [ { "id": 1, @@ -3426,6 +3552,7 @@ export const protoLayers = [ "name": "DeviceResolve", "id": 24, "description": "", + "yaml": "DeviceResolve:\n id: 24\n fields:\n - id: 1\n name: deviceId\n type: string\n - id: 2\n name: solution\n type: DeviceSolution", "fields": [ { "id": 1, @@ -3447,6 +3574,7 @@ export const protoLayers = [ "name": "Sync", "id": 25, "description": "", + "yaml": "Sync:\n id: 25\n fields:\n - id: 1\n name: syncStatus\n type: SyncStatus\n - id: 2\n name: timestamp\n type: int64", "fields": [ { "id": 1, @@ -3468,6 +3596,7 @@ export const protoLayers = [ "name": "SignalPeer", "id": 26, "description": "", + "yaml": "SignalPeer:\n id: 26\n fields:\n - id: 1\n name: src\n type: string\n - id: 2\n name: dst\n type: string\n - id: 3\n name: sharedPublic?\n type: string\n - id: 4\n name: signalType\n type: SignalType\n - id: 5\n name: callId?\n type: string\n - id: 6\n name: joinToken?\n type: string", "fields": [ { "id": 1, @@ -3517,6 +3646,7 @@ export const protoLayers = [ "name": "WebRTC", "id": 27, "description": "", + "yaml": "WebRTC:\n id: 27\n fields:\n - id: 1\n name: sdpOrCandidate\n type: string\n - id: 2\n name: type\n type: WebRTCType", "fields": [ { "id": 1, @@ -3538,6 +3668,7 @@ export const protoLayers = [ "name": "IceServers", "id": 28, "description": "", + "yaml": "IceServers:\n id: 28\n fields:\n - id: 1\n name: iceServers?\n type: RTCIceServer[]", "fields": [ { "id": 1, @@ -3552,6 +3683,7 @@ export const protoLayers = [ "name": "UploadCandidate", "id": 29, "description": "Запрос \"посадочного места\" у сервера при загрузке файла", + "yaml": "UploadCandidate:\n id: 29\n fields:\n - id: 1\n name: size\n type: uint32\n - id: 2\n name: objectId?\n type: string\n - id: 3\n name: chunkLength?\n type: uint32\n - id: 4\n name: chunks?\n type: uint32\n - id: 5\n name: host?\n type: string\n - id: 6\n name: ttl?\n type: uint32", "fields": [ { "id": 1, @@ -3601,6 +3733,7 @@ export const protoLayers = [ "name": "DownloadCandidate", "id": 30, "description": "Запрос скачивания файла с сервера, нужно передать objectId, а дальнейшую информацию вернет сервер", + "yaml": "DownloadCandidate:\n id: 30\n fields:\n - id: 1\n name: size?\n type: uint32\n - id: 2\n name: objectId\n type: string\n - id: 3\n name: chunkLength?\n type: uint32\n - id: 4\n name: chunks?\n type: uint32\n - id: 5\n name: host?\n type: string", "fields": [ { "id": 1, @@ -3643,6 +3776,7 @@ export const protoLayers = [ "name": "SetAvatar", "id": 31, "description": "Установить аватар профиля", + "yaml": "SetAvatar:\n id: 31\n fields:\n - id: 1\n name: objectId\n type: string\n - id: 2\n name: blurhash\n type: string\n - id: 3\n name: mediaKey\n type: string", "fields": [ { "id": 1, @@ -3671,6 +3805,7 @@ export const protoLayers = [ "name": "RemoveAvatar", "id": 32, "description": "Удаляет аватар или фотографию из профиля", + "yaml": "RemoveAvatar:\n id: 32\n fields:\n - id: 1\n name: objectId\n type: string", "fields": [ { "id": 1, @@ -3685,6 +3820,7 @@ export const protoLayers = [ "name": "Profile", "id": 33, "description": "Запрос профиля", + "yaml": "Profile:\n id: 33\n fields:\n - id: 1\n name: publicKey\n type: string\n - id: 2\n name: info?\n type: ProfileInfo", "fields": [ { "id": 1, diff --git a/src/pages/EncryptionPage.module.css b/src/pages/EncryptionPage.module.css new file mode 100644 index 0000000..37b8ccf --- /dev/null +++ b/src/pages/EncryptionPage.module.css @@ -0,0 +1,162 @@ +.page { + padding: 48px 0 112px; + background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-9)); +} + +.hero, +.notice, +.contents, +.section { + width: 100%; + max-width: 900px; + margin-inline: auto; +} + +.hero { + padding: 64px 0 48px; +} + +.hero h1 { + margin-top: var(--mantine-spacing-lg); + color: light-dark(var(--mantine-color-black), var(--mantine-color-white)); + font-size: clamp(48px, 7vw, 74px); + font-weight: 600; + letter-spacing: -0.045em; + line-height: 1.02; +} + +.heroDescription { + max-width: 790px; + margin-top: var(--mantine-spacing-lg); + font-size: 19px; + line-height: 1.7; +} + +.notice { + padding: var(--mantine-spacing-xl); + border-left: 4px solid var(--mantine-color-blue-5); + background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-7)); +} + +.contents { + margin-top: var(--mantine-spacing-lg); + padding-bottom: 48px; +} + +.contentLink { + padding: 7px 12px; + border: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-5)); + border-radius: 999px; + background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-7)); + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1)); + font-size: var(--mantine-font-size-xs); + font-weight: 600; + text-decoration: none; +} + +.contentLink:hover { + border-color: var(--mantine-color-blue-4); + color: var(--mantine-color-blue-6); + text-decoration: none; +} + +.section { + scroll-margin-top: 104px; + margin-top: 24px; + padding: 64px clamp(24px, 5vw, 64px); + border: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); + border-radius: var(--mantine-radius-xl); + background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-8)); +} + +.section > h2 { + max-width: 760px; + margin-top: var(--mantine-spacing-xs); + color: light-dark(var(--mantine-color-black), var(--mantine-color-white)); + font-size: clamp(30px, 4vw, 43px); + font-weight: 600; + letter-spacing: -0.035em; + line-height: 1.12; +} + +.sectionBody { + margin-top: 30px; +} + +.sectionBody > h3 { + margin-top: 42px; + margin-bottom: 12px; + color: light-dark(var(--mantine-color-dark-9), var(--mantine-color-white)); + font-size: 21px; + font-weight: 650; +} + +.codeBlock { + margin: var(--mantine-spacing-lg) 0; + padding: 18px 20px; + border: 1px solid light-dark(var(--mantine-color-blue-1), var(--mantine-color-blue-9)); + border-radius: var(--mantine-radius-md); + background: light-dark(var(--mantine-color-blue-0), rgba(34, 139, 230, 0.1)); + color: light-dark(var(--mantine-color-blue-9), var(--mantine-color-blue-1)); + font-size: 13px; + line-height: 1.7; + overflow-x: auto; + white-space: pre; +} + +.list { + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1)); + line-height: 1.7; +} + +.list li::marker { + color: var(--mantine-color-blue-5); +} + +.table { + margin-top: var(--mantine-spacing-md); + border: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); + border-radius: var(--mantine-radius-md); +} + +.table th { + background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7)); +} + +.boundary { + background: light-dark(var(--mantine-color-orange-0), rgba(253, 126, 20, 0.08)); +} + +.terms { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0 32px; + margin: 0; +} + +.term { + padding: 20px 0; + border-bottom: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); +} + +.term dd { + margin: 6px 0 0; +} + +@media (max-width: 48em) { + .page { + padding: 32px 0 80px; + } + + .hero { + padding: 48px 0 40px; + } + + .section { + padding: 42px 24px; + } + + .terms { + grid-template-columns: 1fr; + } +} diff --git a/src/pages/EncryptionPage.tsx b/src/pages/EncryptionPage.tsx new file mode 100644 index 0000000..cc44e8f --- /dev/null +++ b/src/pages/EncryptionPage.tsx @@ -0,0 +1,511 @@ +import { + Anchor, + Badge, + Box, + Code, + Container, + Group, + List, + Paper, + SimpleGrid, + Stack, + Table, + Text, + ThemeIcon, + Title, +} from '@mantine/core'; +import { + IconBinaryTree, + IconFileTextShield, + IconKey, + IconLock, + IconRoute, + IconShieldCheck, +} from '@tabler/icons-react'; +import { Link } from 'react-router-dom'; +import { SEO } from '../components/SEO/SEO'; +import { SiteFooter } from '../components/SiteFooter/SiteFooter'; +import { SiteHeader } from '../components/SiteHeader/SiteHeader'; +import classes from './EncryptionPage.module.css'; + +const MESSAGE_FLOW = [ + { + number: '01', + title: 'Create message material', + description: 'Generate a fresh 32-byte content key and a fresh 24-byte nonce.', + }, + { + number: '02', + title: 'Encrypt the content', + description: 'Protect UTF-8 message content with XChaCha20-Poly1305.', + }, + { + number: '03', + title: 'Wrap for the recipient', + description: 'Encrypt the key and nonce for the recipient using ephemeral secp256k1 ECDH.', + }, + { + number: '04', + title: 'Transmit ciphertext', + description: 'Send the encrypted content, key envelope, routing data, and attachment metadata.', + }, +]; + +const ATTACHMENT_LAYOUT = [ + ['Magic', '4 bytes', 'ASCII RAC2 (52 41 43 32)'], + ['Salt', '16 bytes', 'Random per attachment encryption session'], + ['IV', '12 bytes', 'Random and unique for every encrypted chunk'], + ['Ciphertext + tag', 'Variable', 'AES-256-GCM output including its authentication tag'], +]; + +function TechnicalSection({ + id, + eyebrow, + title, + children, +}: { + id: string; + eyebrow: string; + title: string; + children: React.ReactNode; +}) { + return ( + + + {eyebrow} + + {title} + {children} + + ); +} + +function Term({ + term, + children, +}: { + term: string; + children: React.ReactNode; +}) { + return ( + + + {term} + + + {children} + + + ); +} + +export function EncryptionPage() { + return ( + <> + + +
+ + + Rosetta + + + {' '} + / Encryption + + + + + + + + Implementation reference + + Rosetta Encryption + + A technical description of how the current Rosetta desktop client + derives identity keys, protects direct and group messages, encrypts + attachments, and separates plaintext from server-visible data. + + + + + Scope of this document + + This page documents the current client implementation, including its + compatibility formats. It is an implementation reference, not a claim + of formal verification, an independent security audit, or a frozen + protocol specification. + + + + + {[ + ['Direct messages', '#direct-messages'], + ['Attachments', '#attachments'], + ['Groups', '#groups'], + ['Key material', '#key-material'], + ['Security properties', '#security-properties'], + ['Terminology', '#terminology'], + ].map(([label, href]) => ( + + {label} + + ))} + + + + + Rosetta encrypts the text of each one-to-one message with a newly + generated content key. The symmetric secret is never sent in plaintext: + it is placed in a separate recipient-specific key envelope. + + + + {MESSAGE_FLOW.map(({ number, title, description }) => ( + + + {number} + + + {title} + + + {description} + + + ))} + + + Content cipher + + {`contentKey = randomBytes(32) # 256 bits +nonce = randomBytes(24) # 192 bits +ciphertext = XChaCha20-Poly1305( + key = contentKey, + nonce = nonce, + plaintext = UTF8(message) +)`} + + + XChaCha20-Poly1305 is an AEAD construction: it encrypts the message and + produces an authentication tag. Decryption fails if the protected + ciphertext has been changed or the wrong key is supplied. + + + Recipient key envelope + + + The client generates an ephemeral secp256k1 key pair for the + message. + + + ECDH combines the ephemeral private key with the recipient's + long-term public key. + + + The x-coordinate of the shared point becomes a 32-byte AES key. + + + The 56-byte message material — content key followed by nonce — is + encrypted with AES-CBC and PKCS#7 padding using a random 16-byte IV. + + + The envelope carries the IV, encrypted material, and uncompressed + ephemeral public key. The recipient reconstructs the same shared + secret with their private key. + + + + + {`messageSecret = contentKey || nonce +ephemeral = secp256k1.randomKeyPair() +sharedPoint = ECDH(ephemeral.private, recipient.public) +wrappingKey = sharedPoint.x[0..31] +keyEnvelope = Base64(iv || AES-CBC(wrappingKey, messageSecret) || ephemeral.public)`} + + + What crosses the service boundary + + The message packet contains sender and recipient identifiers, a + timestamp, message ID, encrypted content, the recipient key envelope, + and attachment descriptors. The server can route this packet, but it + does not receive the direct-message content key or plaintext message. + + + + + + Files, images, audio, and video are encrypted before upload. Large + payloads are processed as independent chunks so the client can stream, + retry, cache, and decrypt ranges without holding an entire file in + memory. + + + + {[ + ['AES-256-GCM', 'Authenticated encryption for every binary chunk'], + ['PBKDF2-SHA-256', '100,000 iterations and a 16-byte random salt'], + ['Random IV', 'A fresh 12-byte IV for every chunk'], + ].map(([title, description], index) => { + const Icon = [IconFileTextShield, IconBinaryTree, IconKey][index]; + return ( + + + + + + {title} + + + {description} + + + ); + })} + + + RAC2 encrypted chunk format + + RAC2 || salt[16] || iv[12] || AES-256-GCM-ciphertext-and-tag + + + + + + Segment + Length + Purpose + + + + {ATTACHMENT_LAYOUT.map(([segment, length, purpose]) => ( + + + {segment} + + {length} + + + {purpose} + + + + ))} + +
+
+ + + One random salt and derived AES key are reused within an attachment + encryption session; every chunk receives an independent random IV. + AES-GCM's authentication tag lets the client reject modified or + corrupted chunks before exposing plaintext. The CDN stores encrypted + chunks and object metadata, not the file decryption secret. + +
+ + + + A Rosetta group has a shared secret available to its members. Group + messages do not transmit a new recipient envelope with every message; + the shared group key is used by the client's compatibility codec. + Inline attachment data, sender-readable message copies, and protected + local values use the same codec. + + + {`input + → DEFLATE compression + → PBKDF2(password, "rosetta", 1,000 iterations) + → AES-CBC with random 16-byte IV + → Base64(iv) : Base64(ciphertext)`} + + + This compatibility format provides confidentiality but does not include + an AEAD authentication tag. Binary attachments use the newer RAC2 + AES-GCM format described above. + + + + + + + + + + Account identity + + The recovery phrase is expanded to a BIP39 seed. SHA-256 of that + seed becomes the 32-byte secp256k1 private key, and the client + derives a compressed public key for the account identity. + + + + + + + Local protection + + The private key, recovery phrase, readable message cache, and + sender-side message secret are encrypted locally with material + derived from the user's password or account private key. + The plaintext account password is not sent to the service. + + + + + + + + + + Protected + + + Direct-message plaintext and per-message secret + Binary attachment contents and integrity + Locally stored private key and recovery phrase + Sender-side readable message cache + + + + + Not hidden by end-to-end encryption + + + Sender and recipient or group identifiers + Message timestamps, IDs, and delivery metadata + Attachment object IDs, sizes, and transfer metadata + Network information such as IP addresses and timing + + + + + + + + + +
+ Important implementation boundaries + + Direct-message ciphertext has AEAD integrity, but the + current recipient key envelope is not a digital signature. + Compromise of a recipient's long-term private key can + expose previously captured direct-message key envelopes, so + this design does not claim long-term forward secrecy. + Shared group keys similarly require careful rotation when + membership changes. + +
+
+
+
+ + + + + Authenticated Encryption with Associated Data. A cipher mode that + protects confidentiality and detects unauthorized modification. + + + A stream-cipher-based AEAD construction with a 192-bit nonce, + allowing securely generated random nonces with a very low collision + probability. + + + An AES mode that combines counter-mode encryption with a Galois + authentication tag. Rosetta uses it for RAC2 attachment chunks. + + + Elliptic Curve Diffie–Hellman. Two parties combine a private key + with another party's public key to calculate the same shared + secret without transmitting that secret. + + + The elliptic curve used for Rosetta account keys and ephemeral + direct-message key envelopes. + + + A password-based key derivation function that repeatedly applies a + pseudorandom function with a salt to derive key material. + + + A value that must be unique for a key and encryption operation. + Rosetta generates random nonces and IVs using the platform + cryptographic random number generator. + + + A property in which compromise of a long-term key does not reveal + earlier session keys. The current Rosetta direct-message envelope + does not claim this property. + + + + + Standards and implementation references + + NIST SP 800-38D — GCM and GMAC + + + RFC 8018 — PBKDF2 and PKCS #5 + + + SEC 2 — secp256k1 domain parameters + + + noble-ciphers — XChaCha20-Poly1305 implementation + + + +
+
+ + + ); +} diff --git a/src/pages/LandingPage.module.css b/src/pages/LandingPage.module.css index 05cadc5..61ead0f 100644 --- a/src/pages/LandingPage.module.css +++ b/src/pages/LandingPage.module.css @@ -96,7 +96,7 @@ .featuresSection, .appsSection, -.protocolSection { +.encryptionSection { padding: 112px 0; } @@ -140,12 +140,12 @@ background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-7)); } -.protocolSection { +.encryptionSection { padding-top: 0; background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-8)); } -.protocolCard { +.encryptionCard { display: flex; align-items: center; justify-content: space-between; @@ -154,14 +154,14 @@ background: linear-gradient(135deg, var(--mantine-color-blue-8), var(--mantine-color-blue-5)); } -.protocolCard h2 { +.encryptionCard h2 { font-size: clamp(32px, 4vw, 46px); font-weight: 600; letter-spacing: -0.03em; line-height: 1.1; } -.protocolButton { +.encryptionButton { flex: 0 0 auto; } @@ -207,15 +207,15 @@ .featuresSection, .appsSection, - .protocolSection { + .encryptionSection { padding: 80px 0; } - .protocolSection { + .encryptionSection { padding-top: 0; } - .protocolCard { + .encryptionCard { align-items: flex-start; flex-direction: column; padding: 36px 28px; diff --git a/src/pages/LandingPage.tsx b/src/pages/LandingPage.tsx index 0b6b5c7..fa48e03 100644 --- a/src/pages/LandingPage.tsx +++ b/src/pages/LandingPage.tsx @@ -16,6 +16,7 @@ import { IconArrowDown, IconBolt, IconBrandAndroid, + IconBrandApple, IconBrandWindows, IconCheck, IconCode, @@ -88,6 +89,12 @@ const PLATFORMS: Array<{ description: 'Private messaging for Android phones and tablets.', icon: IconBrandAndroid, }, + { + key: 'darwin', + title: 'Rosetta for macOS', + description: 'Native packages for Apple silicon and Intel-based Macs.', + icon: IconBrandApple, + }, { key: 'win32', title: 'Rosetta for Windows', @@ -168,7 +175,7 @@ function DownloadApps() { - + {PLATFORMS.map(({ key, title, description, icon: Icon }) => { const platformReleases = groupedReleases[key] || []; @@ -387,30 +394,31 @@ export function LandingPage() { - + - + - Rosetta Proto + Rosetta Encryption - The protocol is documented from its source. + How Rosetta protects every message and attachment. - Browse every packet, object, enum, field ID, type reference, and - layer change generated directly from Rosetta YAML schemas. + Follow the complete encryption path from per-message keys and + XChaCha20-Poly1305 to recipient key envelopes and authenticated + attachment chunks. diff --git a/src/pages/ProtoPage.module.css b/src/pages/ProtoPage.module.css index 1bf6836..d6af747 100644 --- a/src/pages/ProtoPage.module.css +++ b/src/pages/ProtoPage.module.css @@ -86,21 +86,23 @@ letter-spacing: -0.02em; } -.signature { +.yamlBlock { margin: var(--mantine-spacing-lg) 0; - padding: 12px 12px 12px 18px; + padding: 12px 12px 16px 18px; border: 1px solid light-dark(var(--mantine-color-blue-1), var(--mantine-color-blue-9)); border-radius: var(--mantine-radius-md); background: light-dark(var(--mantine-color-blue-0), rgba(34, 139, 230, 0.1)); } -.signature code { - flex: 1; +.yamlBlock code { + display: block; padding: 0; background: transparent; color: light-dark(var(--mantine-color-blue-9), var(--mantine-color-blue-1)); - overflow-wrap: anywhere; - white-space: normal; + font-size: 13px; + line-height: 1.65; + overflow-x: auto; + white-space: pre; } .table { diff --git a/src/pages/ProtoPage.tsx b/src/pages/ProtoPage.tsx index 265946a..a645b56 100644 --- a/src/pages/ProtoPage.tsx +++ b/src/pages/ProtoPage.tsx @@ -163,59 +163,44 @@ function FieldTable({ fields, layer }: { fields: ProtoField[]; layer: ProtoLayer ); } -function packetSignature(packet: ProtoPacket) { - const constructor = packet.id.toString(16).padStart(2, '0'); - const fields = packet.fields - .map((field) => `${field.name}${field.optional ? '?' : ''}:${field.type}`) - .join(' '); - return `${packet.name}#${constructor}${fields ? ` ${fields}` : ''}`; -} - -function typeSignature(type: ProtoType) { - const fields = type.fields - .map((field) => `${field.name}${field.optional ? '?' : ''}:${field.type}`) - .join(' '); - return `${type.name}${fields ? ` ${fields}` : ''} = ${type.name}`; -} - -function Signature({ value }: { value: string }) { +function YamlSnippet({ value }: { value: string }) { return ( - + + + + YAML schema + + + {({ copied, copy }) => ( + + + {copied ? : } + + + )} + + {value} - - {({ copied, copy }) => ( - - - {copied ? : } - - - )} - - + ); } function PacketDefinition({ packet, layer }: { packet: ProtoPacket; layer: ProtoLayer }) { return ( - -
- - Packet · ID {packet.id} - - {packet.name} -
- 0x{packet.id.toString(16).padStart(2, '0')} -
+ + Packet · ID {packet.id} + + {packet.name} {packet.description || `Packet ${packet.name} in protocol layer ${layer.version}.`} - +
); @@ -231,7 +216,7 @@ function TypeDefinition({ type, layer }: { type: ProtoType; layer: ProtoLayer }) {type.description || `Object ${type.name} in protocol layer ${layer.version}.`} - +
); @@ -247,7 +232,7 @@ function EnumDefinition({ item, layer }: { item: ProtoEnum; layer: ProtoLayer }) {item.description || `Enumeration ${item.name} in protocol layer ${layer.version}.`} - + @@ -328,7 +313,7 @@ export function ProtoPage() { Rosetta Proto Complete protocol reference generated from the RCC layer schemas. Browse - packet constructors, objects, enumerations, field IDs, optional values, + packet definitions, objects, enumerations, field IDs, optional values, comments, and linked types. @@ -429,7 +414,7 @@ export function ProtoPage() { Packets - Network packet constructors and their serialized fields. + Network packets and their serialized YAML fields. {packets.map((packet) => ( Email{' '} - - Admin@rosetta.im + + Support@rosetta.im {' '} with a description of the concern. Do not include recovery phrases or private keys. @@ -172,7 +172,7 @@ export function SupportPage() {