├── CNAME ├── .gitignore ├── docs ├── icon96.png ├── protocol │ ├── img │ │ ├── impl.png │ │ ├── logo.png │ │ ├── arrow.png │ │ ├── impl16.png │ │ ├── columns.png │ │ ├── favicon.png │ │ ├── key_big_n.png │ │ ├── palette.png │ │ ├── sigchain.png │ │ ├── blobs_fetch.png │ │ ├── rpc_header.png │ │ ├── client_hello.png │ │ ├── follow_graph.png │ │ ├── format_blob_id.png │ │ ├── format_invite.png │ │ ├── message_flow.png │ │ ├── rpc_alignment.png │ │ ├── rpc_overview.png │ │ ├── server_accept.png │ │ ├── server_hello.png │ │ ├── starting_keys.png │ │ ├── blobs_get_slice.png │ │ ├── box_stream_send.png │ │ ├── identity_keypair.png │ │ ├── key_big_a_public.png │ │ ├── key_big_a_secret.png │ │ ├── key_big_b_public.png │ │ ├── key_big_b_secret.png │ │ ├── room_invite_uri.png │ │ ├── room_perspective.png │ │ ├── box_stream_goodbye.png │ │ ├── box_stream_overview.png │ │ ├── box_stream_params.png │ │ ├── box_stream_receive.png │ │ ├── client_authenticate.png │ │ ├── final_shared_secret.png │ │ ├── follow_perspective.png │ │ ├── format_message_id.png │ │ ├── format_public_key.png │ │ ├── key_big_a_little_b.png │ │ ├── key_little_a_big_b.png │ │ ├── key_little_a_public.png │ │ ├── key_little_b_public.png │ │ ├── metafeed-structure.png │ │ ├── format_udp_broadcast.png │ │ ├── key_little_a_little_b.png │ │ ├── private_message_decrypt.png │ │ ├── private_message_encrypt.png │ │ ├── shared_secret_derivation_1.png │ │ ├── shared_secret_derivation_2.png │ │ ├── metafeed-structure-with-feeds.png │ │ ├── private_message_starting_keys.png │ │ ├── private_message_secret_derivation.png │ │ ├── metafeed-structure-with-feeds-simplified.png │ │ ├── arrow.svg │ │ ├── room_invite_uri.svg │ │ ├── format_public_key.svg │ │ ├── format_message_id.svg │ │ ├── format_blob_id.svg │ │ ├── format_udp_broadcast.svg │ │ ├── impl.svg │ │ └── room_perspective.svg │ ├── metadata.yaml │ ├── introduction.html │ ├── discovery.html │ └── cryptography.html ├── simple.css ├── index.html └── message_types │ └── index.html ├── templates └── simple │ ├── icon96.png │ ├── simple.html │ └── simple.css ├── content ├── protocol │ ├── img │ │ ├── arrow.png │ │ ├── impl.png │ │ ├── impl16.png │ │ ├── logo.png │ │ ├── columns.png │ │ ├── favicon.png │ │ ├── palette.png │ │ ├── sigchain.png │ │ ├── blobs_fetch.png │ │ ├── key_big_n.png │ │ ├── rpc_header.png │ │ ├── client_hello.png │ │ ├── follow_graph.png │ │ ├── format_invite.png │ │ ├── message_flow.png │ │ ├── rpc_alignment.png │ │ ├── rpc_overview.png │ │ ├── server_accept.png │ │ ├── server_hello.png │ │ ├── starting_keys.png │ │ ├── blobs_get_slice.png │ │ ├── box_stream_send.png │ │ ├── format_blob_id.png │ │ ├── identity_keypair.png │ │ ├── key_big_a_public.png │ │ ├── key_big_a_secret.png │ │ ├── key_big_b_public.png │ │ ├── key_big_b_secret.png │ │ ├── room_invite_uri.png │ │ ├── room_perspective.png │ │ ├── box_stream_goodbye.png │ │ ├── box_stream_params.png │ │ ├── box_stream_receive.png │ │ ├── follow_perspective.png │ │ ├── format_message_id.png │ │ ├── format_public_key.png │ │ ├── key_big_a_little_b.png │ │ ├── key_little_a_big_b.png │ │ ├── metafeed-structure.png │ │ ├── box_stream_overview.png │ │ ├── client_authenticate.png │ │ ├── final_shared_secret.png │ │ ├── format_udp_broadcast.png │ │ ├── key_little_a_little_b.png │ │ ├── key_little_a_public.png │ │ ├── key_little_b_public.png │ │ ├── private_message_decrypt.png │ │ ├── private_message_encrypt.png │ │ ├── shared_secret_derivation_1.png │ │ ├── shared_secret_derivation_2.png │ │ ├── metafeed-structure-with-feeds.png │ │ ├── private_message_starting_keys.png │ │ ├── private_message_secret_derivation.png │ │ ├── metafeed-structure-with-feeds-simplified.png │ │ ├── arrow.svg │ │ ├── room_invite_uri.svg │ │ ├── format_public_key.svg │ │ ├── format_message_id.svg │ │ ├── format_blob_id.svg │ │ ├── format_udp_broadcast.svg │ │ └── impl.svg │ ├── configuration.toml │ ├── metadata.yaml │ ├── introduction.md │ ├── discovery.md │ └── cryptography.md ├── configuration.toml ├── specifications │ ├── configuration.toml │ └── README.md ├── message_types │ ├── configuration.toml │ ├── gathering.md │ ├── pub.md │ ├── bookclub.md │ ├── channel.md │ ├── vote.md │ ├── contact.md │ ├── post.md │ ├── private.md │ ├── more-info.md │ ├── README.md │ ├── bookclubUpdate.md │ ├── blog.md │ └── about.md └── README.md ├── scripts ├── lua │ ├── disable_floats.tex │ ├── install_dependencies.sh │ ├── README.md │ ├── execute_inline_lua.lua │ ├── fix_svg.lua │ └── fix_links.lua └── README.md ├── LICENSE ├── .gitmodules ├── README.md └── .github └── workflows └── build_documentation.yml /CNAME: -------------------------------------------------------------------------------- 1 | ssb-docs.soapdog.org -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | -------------------------------------------------------------------------------- /docs/icon96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/icon96.png -------------------------------------------------------------------------------- /docs/protocol/img/impl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/impl.png -------------------------------------------------------------------------------- /docs/protocol/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/logo.png -------------------------------------------------------------------------------- /docs/protocol/img/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/arrow.png -------------------------------------------------------------------------------- /docs/protocol/img/impl16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/impl16.png -------------------------------------------------------------------------------- /templates/simple/icon96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/templates/simple/icon96.png -------------------------------------------------------------------------------- /content/protocol/img/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/arrow.png -------------------------------------------------------------------------------- /content/protocol/img/impl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/impl.png -------------------------------------------------------------------------------- /content/protocol/img/impl16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/impl16.png -------------------------------------------------------------------------------- /content/protocol/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/logo.png -------------------------------------------------------------------------------- /docs/protocol/img/columns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/columns.png -------------------------------------------------------------------------------- /docs/protocol/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/favicon.png -------------------------------------------------------------------------------- /docs/protocol/img/key_big_n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/key_big_n.png -------------------------------------------------------------------------------- /docs/protocol/img/palette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/palette.png -------------------------------------------------------------------------------- /docs/protocol/img/sigchain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/sigchain.png -------------------------------------------------------------------------------- /content/protocol/img/columns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/columns.png -------------------------------------------------------------------------------- /content/protocol/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/favicon.png -------------------------------------------------------------------------------- /content/protocol/img/palette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/palette.png -------------------------------------------------------------------------------- /content/protocol/img/sigchain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/sigchain.png -------------------------------------------------------------------------------- /docs/protocol/img/blobs_fetch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/blobs_fetch.png -------------------------------------------------------------------------------- /docs/protocol/img/rpc_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/rpc_header.png -------------------------------------------------------------------------------- /content/protocol/img/blobs_fetch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/blobs_fetch.png -------------------------------------------------------------------------------- /content/protocol/img/key_big_n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/key_big_n.png -------------------------------------------------------------------------------- /content/protocol/img/rpc_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/rpc_header.png -------------------------------------------------------------------------------- /docs/protocol/img/client_hello.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/client_hello.png -------------------------------------------------------------------------------- /docs/protocol/img/follow_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/follow_graph.png -------------------------------------------------------------------------------- /docs/protocol/img/format_blob_id.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/format_blob_id.png -------------------------------------------------------------------------------- /docs/protocol/img/format_invite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/format_invite.png -------------------------------------------------------------------------------- /docs/protocol/img/message_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/message_flow.png -------------------------------------------------------------------------------- /docs/protocol/img/rpc_alignment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/rpc_alignment.png -------------------------------------------------------------------------------- /docs/protocol/img/rpc_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/rpc_overview.png -------------------------------------------------------------------------------- /docs/protocol/img/server_accept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/server_accept.png -------------------------------------------------------------------------------- /docs/protocol/img/server_hello.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/server_hello.png -------------------------------------------------------------------------------- /docs/protocol/img/starting_keys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/starting_keys.png -------------------------------------------------------------------------------- /content/protocol/img/client_hello.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/client_hello.png -------------------------------------------------------------------------------- /content/protocol/img/follow_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/follow_graph.png -------------------------------------------------------------------------------- /content/protocol/img/format_invite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/format_invite.png -------------------------------------------------------------------------------- /content/protocol/img/message_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/message_flow.png -------------------------------------------------------------------------------- /content/protocol/img/rpc_alignment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/rpc_alignment.png -------------------------------------------------------------------------------- /content/protocol/img/rpc_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/rpc_overview.png -------------------------------------------------------------------------------- /content/protocol/img/server_accept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/server_accept.png -------------------------------------------------------------------------------- /content/protocol/img/server_hello.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/server_hello.png -------------------------------------------------------------------------------- /content/protocol/img/starting_keys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/starting_keys.png -------------------------------------------------------------------------------- /docs/protocol/img/blobs_get_slice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/blobs_get_slice.png -------------------------------------------------------------------------------- /docs/protocol/img/box_stream_send.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/box_stream_send.png -------------------------------------------------------------------------------- /docs/protocol/img/identity_keypair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/identity_keypair.png -------------------------------------------------------------------------------- /docs/protocol/img/key_big_a_public.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/key_big_a_public.png -------------------------------------------------------------------------------- /docs/protocol/img/key_big_a_secret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/key_big_a_secret.png -------------------------------------------------------------------------------- /docs/protocol/img/key_big_b_public.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/key_big_b_public.png -------------------------------------------------------------------------------- /docs/protocol/img/key_big_b_secret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/key_big_b_secret.png -------------------------------------------------------------------------------- /docs/protocol/img/room_invite_uri.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/room_invite_uri.png -------------------------------------------------------------------------------- /docs/protocol/img/room_perspective.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/room_perspective.png -------------------------------------------------------------------------------- /content/protocol/img/blobs_get_slice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/blobs_get_slice.png -------------------------------------------------------------------------------- /content/protocol/img/box_stream_send.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/box_stream_send.png -------------------------------------------------------------------------------- /content/protocol/img/format_blob_id.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/format_blob_id.png -------------------------------------------------------------------------------- /content/protocol/img/identity_keypair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/identity_keypair.png -------------------------------------------------------------------------------- /content/protocol/img/key_big_a_public.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/key_big_a_public.png -------------------------------------------------------------------------------- /content/protocol/img/key_big_a_secret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/key_big_a_secret.png -------------------------------------------------------------------------------- /content/protocol/img/key_big_b_public.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/key_big_b_public.png -------------------------------------------------------------------------------- /content/protocol/img/key_big_b_secret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/key_big_b_secret.png -------------------------------------------------------------------------------- /content/protocol/img/room_invite_uri.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/room_invite_uri.png -------------------------------------------------------------------------------- /content/protocol/img/room_perspective.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/room_perspective.png -------------------------------------------------------------------------------- /docs/protocol/img/box_stream_goodbye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/box_stream_goodbye.png -------------------------------------------------------------------------------- /docs/protocol/img/box_stream_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/box_stream_overview.png -------------------------------------------------------------------------------- /docs/protocol/img/box_stream_params.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/box_stream_params.png -------------------------------------------------------------------------------- /docs/protocol/img/box_stream_receive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/box_stream_receive.png -------------------------------------------------------------------------------- /docs/protocol/img/client_authenticate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/client_authenticate.png -------------------------------------------------------------------------------- /docs/protocol/img/final_shared_secret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/final_shared_secret.png -------------------------------------------------------------------------------- /docs/protocol/img/follow_perspective.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/follow_perspective.png -------------------------------------------------------------------------------- /docs/protocol/img/format_message_id.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/format_message_id.png -------------------------------------------------------------------------------- /docs/protocol/img/format_public_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/format_public_key.png -------------------------------------------------------------------------------- /docs/protocol/img/key_big_a_little_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/key_big_a_little_b.png -------------------------------------------------------------------------------- /docs/protocol/img/key_little_a_big_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/key_little_a_big_b.png -------------------------------------------------------------------------------- /docs/protocol/img/key_little_a_public.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/key_little_a_public.png -------------------------------------------------------------------------------- /docs/protocol/img/key_little_b_public.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/key_little_b_public.png -------------------------------------------------------------------------------- /docs/protocol/img/metafeed-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/metafeed-structure.png -------------------------------------------------------------------------------- /content/protocol/img/box_stream_goodbye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/box_stream_goodbye.png -------------------------------------------------------------------------------- /content/protocol/img/box_stream_params.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/box_stream_params.png -------------------------------------------------------------------------------- /content/protocol/img/box_stream_receive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/box_stream_receive.png -------------------------------------------------------------------------------- /content/protocol/img/follow_perspective.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/follow_perspective.png -------------------------------------------------------------------------------- /content/protocol/img/format_message_id.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/format_message_id.png -------------------------------------------------------------------------------- /content/protocol/img/format_public_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/format_public_key.png -------------------------------------------------------------------------------- /content/protocol/img/key_big_a_little_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/key_big_a_little_b.png -------------------------------------------------------------------------------- /content/protocol/img/key_little_a_big_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/key_little_a_big_b.png -------------------------------------------------------------------------------- /content/protocol/img/metafeed-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/metafeed-structure.png -------------------------------------------------------------------------------- /docs/protocol/img/format_udp_broadcast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/format_udp_broadcast.png -------------------------------------------------------------------------------- /docs/protocol/img/key_little_a_little_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/key_little_a_little_b.png -------------------------------------------------------------------------------- /content/protocol/img/box_stream_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/box_stream_overview.png -------------------------------------------------------------------------------- /content/protocol/img/client_authenticate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/client_authenticate.png -------------------------------------------------------------------------------- /content/protocol/img/final_shared_secret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/final_shared_secret.png -------------------------------------------------------------------------------- /content/protocol/img/format_udp_broadcast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/format_udp_broadcast.png -------------------------------------------------------------------------------- /content/protocol/img/key_little_a_little_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/key_little_a_little_b.png -------------------------------------------------------------------------------- /content/protocol/img/key_little_a_public.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/key_little_a_public.png -------------------------------------------------------------------------------- /content/protocol/img/key_little_b_public.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/key_little_b_public.png -------------------------------------------------------------------------------- /docs/protocol/img/private_message_decrypt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/private_message_decrypt.png -------------------------------------------------------------------------------- /docs/protocol/img/private_message_encrypt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/private_message_encrypt.png -------------------------------------------------------------------------------- /content/protocol/img/private_message_decrypt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/private_message_decrypt.png -------------------------------------------------------------------------------- /content/protocol/img/private_message_encrypt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/private_message_encrypt.png -------------------------------------------------------------------------------- /docs/protocol/img/shared_secret_derivation_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/shared_secret_derivation_1.png -------------------------------------------------------------------------------- /docs/protocol/img/shared_secret_derivation_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/shared_secret_derivation_2.png -------------------------------------------------------------------------------- /content/protocol/img/shared_secret_derivation_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/shared_secret_derivation_1.png -------------------------------------------------------------------------------- /content/protocol/img/shared_secret_derivation_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/shared_secret_derivation_2.png -------------------------------------------------------------------------------- /docs/protocol/img/metafeed-structure-with-feeds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/metafeed-structure-with-feeds.png -------------------------------------------------------------------------------- /docs/protocol/img/private_message_starting_keys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/private_message_starting_keys.png -------------------------------------------------------------------------------- /content/protocol/img/metafeed-structure-with-feeds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/metafeed-structure-with-feeds.png -------------------------------------------------------------------------------- /content/protocol/img/private_message_starting_keys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/private_message_starting_keys.png -------------------------------------------------------------------------------- /docs/protocol/img/private_message_secret_derivation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/private_message_secret_derivation.png -------------------------------------------------------------------------------- /content/protocol/img/private_message_secret_derivation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/private_message_secret_derivation.png -------------------------------------------------------------------------------- /docs/protocol/img/metafeed-structure-with-feeds-simplified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/docs/protocol/img/metafeed-structure-with-feeds-simplified.png -------------------------------------------------------------------------------- /content/protocol/img/metafeed-structure-with-feeds-simplified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soapdog/ssb-documentation/HEAD/content/protocol/img/metafeed-structure-with-feeds-simplified.png -------------------------------------------------------------------------------- /content/protocol/configuration.toml: -------------------------------------------------------------------------------- 1 | title = "Protocol Guide" 2 | toc_depth = 2 3 | 4 | files = [ 5 | "introduction.md", 6 | "cryptography.md", 7 | "discovery.md", 8 | "peer_connections.md", 9 | ] -------------------------------------------------------------------------------- /content/protocol/metadata.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | title: Secure Scuttlebutt Protocol Guide 3 | license: Public Domain 4 | spine: 5 | - introduction.md 6 | - cryptography.md 7 | - discovery.md 8 | - peer_connections.md 9 | --- -------------------------------------------------------------------------------- /docs/protocol/metadata.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | title: Secure Scuttlebutt Protocol Guide 3 | license: Public Domain 4 | spine: 5 | - introduction.md 6 | - cryptography.md 7 | - discovery.md 8 | - peer_connections.md 9 | --- -------------------------------------------------------------------------------- /scripts/lua/disable_floats.tex: -------------------------------------------------------------------------------- 1 | \usepackage{float} 2 | \let\origfigure\figure 3 | \let\endorigfigure\endfigure 4 | \renewenvironment{figure}[1][2] { 5 | \expandafter\origfigure\expandafter[H] 6 | } { 7 | \endorigfigure 8 | } -------------------------------------------------------------------------------- /content/configuration.toml: -------------------------------------------------------------------------------- 1 | files = [ 2 | "README.md", 3 | "protocol", 4 | "specifications", 5 | "message_types", 6 | "development/js", 7 | "development/rust", 8 | "development/go", 9 | "software/patchwork", 10 | "software/manyverse", 11 | "software/planetary", 12 | "software/patchfoo", 13 | "software/patchfox", 14 | ] -------------------------------------------------------------------------------- /content/specifications/configuration.toml: -------------------------------------------------------------------------------- 1 | title = "Specifications" 2 | toc_depth = 1 3 | 4 | files = [ 5 | "README.md", 6 | "bendy-butt-spec", 7 | "envelope-spec", 8 | "private-group-spec", 9 | "ssb-bfe-spec", 10 | "ssb-buttwoo-spec", 11 | "ssb-meta-feed-group-spec", 12 | "ssb-meta-feeds-spec", 13 | "ssb-uri-spec" 14 | ] -------------------------------------------------------------------------------- /content/message_types/configuration.toml: -------------------------------------------------------------------------------- 1 | title = "Message Types" 2 | toc_depth = 1 3 | 4 | files = [ 5 | "README.md", 6 | "about.md", 7 | "blog.md", 8 | "bookclub.md", 9 | "bookclubUpdate.md", 10 | "channel.md", 11 | "contact.md", 12 | "gathering.md", 13 | "more-info.md", 14 | "post.md", 15 | "private.md", 16 | "pub.md", 17 | "vote.md", 18 | ] -------------------------------------------------------------------------------- /scripts/lua/install_dependencies.sh: -------------------------------------------------------------------------------- 1 | #!env bash 2 | 3 | if ! luarocks config --lua-ver | grep '5\.[3456789]' 4 | then 5 | echo 'please first install lua >=5.3 with luarocks' 6 | exit -1 7 | fi 8 | 9 | luarocks install --local lfs 10 | luarocks install --local penlight 11 | luarocks install --local lua-toml 12 | luarocks install --local f-strings 13 | 14 | -------------------------------------------------------------------------------- /content/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Welcome to SSB Documentation 3 | ... 4 | 5 | # Secure Scuttlebutt {#index} 6 | 7 | Welcome to the SSB documentation. At the moment this is closed for renovations. In the meanwhile check the work-in-progress sections: 8 | 9 | * [The Protocol Guide](protocol/introduction.md) 10 | * [Message Types](message_types/README.md) 11 | * [SSB Specifications](specifications/README.md) 12 | 13 | -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # Scripts 2 | 3 | Instead of forcing everyone into my stack of choice, I'm providing different versions of the scripts used to build the documentation. They should all generate the same files — they'll invoke [pandoc](https://pandoc.org) with the same arguments in the end — but they'll run under different runtimes. 4 | 5 | I tend to favour Lua over JS mostly because Lua is simpler and packaged for most systems. On the other hand, most developers on the SSB ecosystem are familiar with JS. 6 | 7 | If you'd rather have a different set of scripts to build the docs, feel free to send a PR. -------------------------------------------------------------------------------- /scripts/lua/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This folder contains the [Lua](https://lua.org) version of the scripts used to build the documentation. 4 | 5 | > **ATTENTION:** At the moment, the build script has a hardcoded call to `mkdir -p` which ties it to UNIX-like systems. This will be removed in favour of a cross-platform solution later. 6 | 7 | ## Requirements 8 | 9 | * [Lua >= 5.3](https://lua.org): The runtime for the Lua programming language. 10 | * [Luarocks](https://luarocks.org): The package manager for Lua. 11 | 12 | ## Installing the dependencies 13 | 14 | Run: 15 | 16 | ``` 17 | $ install_dependencies.sh 18 | ``` -------------------------------------------------------------------------------- /content/protocol/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | [Scuttlebutt](https://www.scuttlebutt.nz/) is a protocol for building decentralized applications that work well offline and that no one person can control. Because there is no central server, Scuttlebutt clients connect to their peers to exchange information. This guide describes the protocols used to communicate within the Scuttlebutt network. 4 | 5 | Scuttlebutt is a flexible protocol, capable of supporting many different types of applications. One of its first applications was as a social network. This guide has a slight focus on how to use Scuttlebutt for social networking, but many of the explanations will still be useful if want to use it for something completely different, or are just curious how it works. -------------------------------------------------------------------------------- /content/message_types/gathering.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Gathering messages 3 | ... 4 | 5 | # `gathering` message 6 | 7 | messages of type `gathering` are used to create _events_. They're analogous to _Facebook events_ if you're familiar with those. 8 | 9 | A gathering message looks like: 10 | 11 | ```{.json} 12 | { 13 | "author": "@GF214rVSdwf/nARy7oDh+AxJViVCGfKqw+aa162itZY=.ed25519", 14 | "content": { 15 | "type": "gathering" 16 | }, 17 | "hash": "sha256", 18 | "previous": "%CExarKlCQwiC2FHodXzJzYQyOFWperqRifMakqT5o/w=.sha256", 19 | "sequence": 14, 20 | "signature": "Q9I0YNyOKrFhQ8/crZqcC7mhIAtlgbgEXPpvG3GcIES7KCOfoZfS0MfpjTHb4rmIMMvzwaouA4oHxUTlyID9Dg==.sig.ed25519", 21 | "timestamp": 1513623739118 22 | } 23 | 24 | ``` 25 | 26 | It looks a bit empty right? That is because the clients use [about](/message_types/about) messages to fill in the details about the event. 27 | -------------------------------------------------------------------------------- /scripts/lua/execute_inline_lua.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | This is a Pandoc filter to execute inline Lua codeblocks in Markdown files. 3 | 4 | This only executes if the Markdown file has "dynamic" set to true in its metatada 5 | --]] 6 | 7 | local dynamic = false 8 | 9 | function Meta(m) 10 | dynamic = m["dynamic"] 11 | 12 | return m 13 | end 14 | 15 | function dump(o) 16 | if type(o) == 'table' then 17 | local s = '{ ' 18 | for k,v in pairs(o) do 19 | if type(k) ~= 'number' then k = '"'..k..'"' end 20 | s = s .. '['..k..'] = ' .. dump(v) .. ',' 21 | end 22 | return s .. '} ' 23 | else 24 | return tostring(o) 25 | end 26 | end 27 | 28 | 29 | 30 | function CodeBlock(el) 31 | if FORMAT:match "html5" then 32 | if el.classes[1] == "lua" and dynamic then 33 | f = load(el.text) 34 | return pandoc.RawBlock("html5", f()) 35 | end 36 | end 37 | 38 | return el 39 | end 40 | 41 | 42 | return { 43 | { Meta = Meta }, 44 | { CodeBlock = CodeBlock} 45 | } -------------------------------------------------------------------------------- /content/message_types/pub.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Pub messages 3 | ... 4 | 5 | # `pub` message 6 | 7 | `pub` messages are used for _pub announcements_. These are issued by [pub servers](https://github.com/ssbc/ssb-server/wiki/pub-servers) so that clients discover them. 8 | 9 | ## What they look like 10 | 11 | ```{.json} 12 | { 13 | "author": "@E5lOaTD+74yeVZhyGnPW9wSykpUGj6h8OjgVIoD4QdI=.ed25519", 14 | "content": { 15 | "address": { 16 | "host": "188.166.252.233", 17 | "key": "@uRECWB4KIeKoNMis2UYWyB2aQPvWmS3OePQvBj2zClg=.ed25519", 18 | "port": 8008 19 | }, 20 | "type": "pub" 21 | }, 22 | "hash": "sha256", 23 | "previous": "%c6bhBJfl1zWABSmV0sVWlTkklYTLxGHfSxt2LwA1ndM=.sha256", 24 | "sequence": 6, 25 | "signature": "4zTxKyyu8Gt24AGwTUrJO8FQ0Xit5OhgHTX+u6eB4FgNTK1ugKnEy7x91657+dQrCwjLHrYuE156/dy9cTpvCA==.sig.ed25519", 26 | "timestamp": 1459981972234 27 | } 28 | ``` 29 | 30 | They match a _pub key_ with an accessible _host_. You can learn more about [pub messages online](http://scuttlebot.io/docs/message-types/pub.html). 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Andre Alves Garzia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /scripts/lua/fix_svg.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | This is a Pandoc filter to fix path references for runtime generated images. 3 | 4 | At the moment it is needed for the mermaid diagrams. 5 | --]] 6 | 7 | function string:endswith(ending) 8 | return ending == "" or self:sub(-#ending) == ending 9 | end 10 | 11 | function string:startswith(start) 12 | return self:sub(1, #start) == start 13 | end 14 | 15 | function loadContent(file) 16 | local f = assert(io.open(file, "rb")) 17 | local content = f:read("*all") 18 | f:close() 19 | return content 20 | end 21 | 22 | 23 | function Image(el) 24 | local old = el.src 25 | if FORMAT:match "html5" then 26 | -- remove references to the content folder 27 | if el.src:startswith "content/" then 28 | el.src = el.src:gsub("content/","") 29 | end 30 | -- if the image is an SVG, inline it because clickevents only work if it is an inline SVG. 31 | if el.src:endswith ".svg" then 32 | if io.open(old, "rb") ~= nil then 33 | local svg = loadContent(old) 34 | return pandoc.RawInline("html5", svg) 35 | end 36 | end 37 | end 38 | 39 | return el 40 | end 41 | -------------------------------------------------------------------------------- /scripts/lua/fix_links.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | This is a Pandoc filter to fix internal links in both the PDF and the HTML. 3 | 4 | To make this work, the link need to be pointing at a file and there must be an anchor 5 | at the top of the target file that matches the normalised name of the file sans the extension 6 | 7 | Ie: a file called "overview.md" there should be a "{#overview}" at the first H1 header in that 8 | file. 9 | --]] 10 | function string:endswith(ending) 11 | return ending == "" or self:sub(-#ending) == ending 12 | end 13 | 14 | function Link(el) 15 | local old = el.target 16 | 17 | if FORMAT:match "latex" then 18 | if el.target:endswith ".md" then 19 | -- local link 20 | local newLink = el.target:gsub(".md","") 21 | el.target = ("#" .. newLink) 22 | end 23 | end 24 | 25 | if FORMAT:match "html5" then 26 | if el.target:endswith ".md" then 27 | -- local link 28 | el.target = el.target:gsub("README.md","index.html") 29 | el.target = el.target:gsub("readme.md","index.html") 30 | el.target = el.target:gsub(".md",".html") 31 | end 32 | end 33 | 34 | -- print(old .. " ---> " .. el.target) 35 | 36 | return el 37 | end -------------------------------------------------------------------------------- /content/message_types/bookclub.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Book Club messages 3 | ... 4 | 5 | # `bookclub` message 6 | 7 | Book club messages are used to create new books. 8 | 9 | A book club message looks like: 10 | 11 | ```{.json} 12 | { 13 | "type": "bookclub", 14 | "title": "A Prehistory of the Cloud", 15 | "authors": "Tung Hui-Hu", 16 | "description": "A radical collage of internet history. Traces the origins of \"the cloud\" as an idea and as a force in the world today. Tung Hui-Hu is a network engineer and English professor, and so the history draws equally from computing and railroads and victorian sewers and 1970's countercultural performance artists. A powerful, pleasantly short read!", 17 | "image": { 18 | "link": "&Qf54gh2QQGhYjC+L1/asC9qEpYeBN76LCwauZmjgioM=.sha256", 19 | "name": "prehistory-of-the-cloud.jpg", 20 | "size": 26228, 21 | "type": "image/jpeg" 22 | } 23 | } 24 | 25 | ``` 26 | 27 | Book club features were built to provide SSB users a convenient way to organise and review books. In this way, it provides the groundwork for experiences similar to book review services such as _Goodreads_ and _BookWyrm_. 28 | 29 | You can read more about book club schemas in the [scuttle-book documentation](https://github.com/ssbc/ssb-book-schema/). 30 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "content/specifications/ssb-uri-spec"] 2 | path = content/specifications/ssb-uri-spec 3 | url = https://github.com/ssbc/ssb-uri-spec.git 4 | [submodule "content/specifications/ssb-bfe-spec"] 5 | path = content/specifications/ssb-bfe-spec 6 | url = https://github.com/ssbc/ssb-bfe-spec.git 7 | [submodule "content/specifications/envelope-spec"] 8 | path = content/specifications/envelope-spec 9 | url = https://github.com/ssbc/envelope-spec.git 10 | [submodule "content/specifications/private-group-spec"] 11 | path = content/specifications/private-group-spec 12 | url = https://github.com/ssbc/private-group-spec.git 13 | [submodule "content/specifications/ssb-meta-feed-group-spec"] 14 | path = content/specifications/ssb-meta-feed-group-spec 15 | url = https://github.com/ssbc/ssb-meta-feed-group-spec.git 16 | [submodule "content/specifications/ssb-meta-feeds-spec"] 17 | path = content/specifications/ssb-meta-feeds-spec 18 | url = https://github.com/ssbc/ssb-meta-feeds-spec.git 19 | [submodule "content/specifications/bendy-butt-spec"] 20 | path = content/specifications/bendy-butt-spec 21 | url = https://github.com/ssbc/bendy-butt-spec.git 22 | [submodule "content/specifications/ssb-buttwoo-spec"] 23 | path = content/specifications/ssb-buttwoo-spec 24 | url = https://github.com/ssbc/ssb-buttwoo-spec.git 25 | -------------------------------------------------------------------------------- /content/message_types/channel.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Channel messages 3 | ... 4 | 5 | # `channel` message 6 | 7 | This message is used to _subscribe and unsubscribe from channels_. Be aware that subscribing to a channel doesn't affect [gossiping](/concepts/gossip). Subscriptions are mostly hints used by applications when they need to figure out what to show to their user. A common practice for an application is to show a _public feed_ to the user containing the messages from their followers and the messages in the channels they subscribed to. 8 | 9 | ## What does it looks like? 10 | 11 | ```{.json} 12 | { 13 | "key": "%bh8KkBLWTK6iAUJ3u4g2bfkye40m4vNw3+D6UFi8=.sha256", 14 | "value": { 15 | "previous": "%y+XwGARQFqvlMRnwfberbwr5+93pskchI+h/A=.sha256", 16 | "sequence": 179, 17 | "author": "@zVgfPY/wrpkjebfbkjqlDderiZr7jCbOpR1k=.ed25519", 18 | "timestamp": 1560279944413, 19 | "hash": "sha256", 20 | "content": { 21 | "type": "channel", 22 | "channel": "p2p-berlin", 23 | "subscribed": true 24 | }, 25 | "signature": "958n47tZTbwmDLjCcunaR9F18zAHOJeMt01P02OrPzaS9y6cjXpeq5/uh9zDhzfVCt5I4hsukLXkdjOSVZNMAQ==.sig.ed25519" 26 | }, 27 | "timestamp": 1560279947258, 28 | "rts": 1560279944413 29 | } 30 | ``` 31 | 32 | On the message above you can see that the author `subscribed` to the channel called `p2p-berlin`. The `subscribed` field is a boolean that indicates if the author is subscribing or unsubscribing from the channel. 33 | -------------------------------------------------------------------------------- /content/message_types/vote.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Vote messages 3 | ... 4 | 5 | # `vote` message 6 | 7 | This message is akin to _likes_, _hearts_ and _stars_ in social networks. They represent an intention to support the message. Secure Scuttlebutt doesn't mandate a term to be used in this case and some clients will call it _like_ while others call it _dig_, it doesn't matter, the message structure is the same. 8 | 9 | ## What does it looks like? 10 | 11 | ```{.json} 12 | { 13 | "key": "%p3LT5yzsfb/bPizfsgsEruyCeCgtqBY=.sha256", 14 | "value": { 15 | "previous": "%isvGQ2h9qnRiilXNssbXbi9pI+lTa1V4rpXpbT0=.sha256", 16 | "sequence": 549, 17 | "author": "@MRiJ+CvDnD9ZjqunY1oy6sfbsbMDC4Q3tTC8riS3s=.ed25519", 18 | "timestamp": 1560279862309, 19 | "hash": "sha256", 20 | "content": { 21 | "type": "vote", 22 | "channel": "patchfox", 23 | "vote": { 24 | "link": "%gZIjLirxZKxCMmczsbfsUeB+Yuz+4re67TNk=.sha256", 25 | "value": 1, 26 | "expression": "Like" 27 | } 28 | }, 29 | "signature": "eIXFCiv3znd3p7/gtWfxUqTaV2ikHkmzKJiKFBJHPRWFsfbsbV4BnLIECva6waQhRgYTpWc9xD39B12a2DQ==.sig.ed25519" 30 | }, 31 | "timestamp": 1560279864098, 32 | "rts": 1560279862309 33 | } 34 | ``` 35 | 36 | On the message above you can see that the author is doing a _Like_ (as specified by the `expression` field) towards a message specified by the `link` field. The value `1` means a positive outcome, a value `-1` means withdrawing your positive vote from the message. 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ssb-documentation 2 | A repository to contain all of SSB documentation 3 | 4 | 5 | ## Current status 6 | 7 | > **ATTENTION:** There are a lot of hardcoded values in the script inside `scripts`. I'm still 8 | > getting all this to work. Once it is working, I'm going to remove them. 9 | 10 | There is a lot of work going on on the build scripts and the source. Currently, this is not stable at all and it is not working. I just pushed it to the repo so that I can work in the open. 11 | 12 | 13 | ## Folder organisation 14 | 15 | - `content/`: holds all the documentation content. 16 | - `docs/`: holds the generated static website. 17 | - `scripts/`: contains auxiliary scripts to work with this repository and build the static site. 18 | - `templates/`: contains the necessary assets for the static site generation (html templates, css, images, etc). 19 | 20 | # Working with this repository 21 | 22 | Some of the most common tasks someone might want to do are: 23 | 24 | ### Assembling the static site 25 | 26 | ``` 27 | $ ./scripts/lua/build.lua [--verbose] 28 | ``` 29 | 30 | ## Dependencies 31 | 32 | * [Pandoc](https://pandoc.org) 33 | * [Lua](https://lua.org) 34 | * [Luarocks](https://luarocks.org) 35 | * [Mermaid filter](https://github.com/raghur/mermaid-filter) 36 | 37 | Check out `scripts/install_dependencies.sh` to install Lua dependencies. **You need Lua 5.3 (or Lua 5.4).** This is not compatible with LuaJIT or Lua 5.1. 38 | 39 | Make sure you follow all the necessary steps to install and setup Luarocks. 40 | 41 | NodeJS is needed for the pandoc mermaid filters. 42 | -------------------------------------------------------------------------------- /content/message_types/contact.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Contact messages 3 | ... 4 | 5 | # `contact` message 6 | 7 | Contact messages are used to follow or unfollow another user. This affects your [social graph](/concepts/social_graph) and [gossiping](/concepts/gossip). Once you follow a given user, you'll start replicating all of that user's feed and, depending on how your client is configured, your followers' friends. Most clients will do two levels of replication, so you end up with all your friends' data and friends-of-friends' data, much like the physical world in which you'll often hear gossip regarding your friends' friends. 8 | 9 | ## What does it look like? 10 | 11 | ```{.json} 12 | { 13 | "key": "%JJW6l3PphHHNxkbe6q+gzsgsgOIgIEjqtnKVq5fo=.sha256", 14 | "value": { 15 | "previous": "%4UrrxMHmjgkw/tDZ40ztxwwrg2TQSzPVMw9efqj9E=.sha256", 16 | "sequence": 1182, 17 | "author": "@GLH9VPzvvU2KcnnUwgwgTUtzw+Rk6fd/Kb9Si0=.ed25519", 18 | "timestamp": 1560279915710, 19 | "hash": "sha256", 20 | "content": { 21 | "type": "contact", 22 | "contact": "@S954DSMnCh8aBqwegwegVZSBtK9N49Wq5AHh3OwOjo=.ed25519", 23 | "following": true 24 | }, 25 | "signature": "laHTnqQkbem2rFxvfwegwegwI4B7l2BE8n60sWsW8UZ/H6B1xz1yhlFGJ/2NkIBGsxpIW7GJM4i8uTCDg==.sig.ed25519" 26 | }, 27 | "timestamp": 1560279930388, 28 | "rts": 1560279915710 29 | } 30 | ``` 31 | 32 | In the message above, the author is _following_ (specified by the `following` field) a user specified by the `contact` field. If the value of the `following` field was false, that would be an unfollow message. -------------------------------------------------------------------------------- /templates/simple/simple.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $for(author-meta)$ 8 | 9 | $endfor$ 10 | $if(date-meta)$ 11 | 12 | $endif$ 13 | $if(keywords)$ 14 | 15 | $endif$ 16 | $if(description-meta)$ 17 | 18 | $endif$ 19 | $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ 20 | 23 | $for(css)$ 24 | 25 | $endfor$ 26 | $for(header-includes)$ 27 | $header-includes$ 28 | $endfor$ 29 | $if(math)$ 30 | $math$ 31 | $endif$ 32 | 35 | 36 | 37 | $for(include-before)$ 38 | $include-before$ 39 | $endfor$ 40 | 41 |
42 | $if(toc)$ 43 | 57 | $endif$ 58 |
59 | $body$ 60 |
61 |
62 | $for(include-after)$ 63 | $include-after$ 64 | $endfor$ 65 | 66 | 67 | -------------------------------------------------------------------------------- /content/specifications/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: SSB Specifications 3 | 4 | dynamic: true 5 | ... 6 | 7 | # Relationship between specs {#index} 8 | 9 | ```{.mermaid format=svg loc=content/specifications/img} 10 | flowchart BT 11 | 12 | subgraph feedFormat [feed format] 13 | classic 14 | bendybutt 15 | buttwoo 16 | end 17 | click bendybutt "/specifications/bendy-butt-spec" " " 18 | click classic "https://ssbc.github.io/scuttlebutt-protocol-guide/#message-format" " " 19 | click buttwoo "/specifications/ssb-buttwoo-spec" " " 20 | 21 | subgraph connection 22 | direction BT 23 | shs[secret handshake] --> box-stream --> muxrpc 24 | end 25 | click shs "https://ssbc.github.io/scuttlebutt-protocol-guide/#handshake" " " 26 | click box-stream "https://ssbc.github.io/scuttlebutt-protocol-guide/#box-stream" " " 27 | click muxrpc "https://ssbc.github.io/scuttlebutt-protocol-guide/#rpc-protocol" " " 28 | 29 | 30 | subgraph replication 31 | direction BT 32 | createHistoryStream:::sunset 33 | EBT[epidemic broadcast trees]:::noSpec 34 | end 35 | click createHistoryStream "https://ssbc.github.io/scuttlebutt-protocol-guide/#createHistoryStream" " " 36 | 37 | 38 | muxrpc --> createHistoryStream & EBT 39 | 40 | 41 | bendybutt --> meta-feeds 42 | envelope --> private-group 43 | 44 | meta-feeds & private-group --> meta-feed-groups 45 | 46 | bfe -.-> envelope 47 | uri -.-> bendybutt & buttwoo 48 | 49 | 50 | 51 | click uri "/specifications/ssb-uri-spec" " " 52 | 53 | 54 | click bfe "/specifications/ssb-bfe-spec" " " 55 | click envelope "/specifications/envelope-spec" " " 56 | click private-group "/specifications/private-group-spec" " " 57 | 58 | 59 | click meta-feeds "/specifications/ssb-meta-feeds-spec" " " 60 | click meta-feed-groups "/specifications/ssb-meta-feed-group-spec" 61 | 62 | classDef node color:#fff, stroke:none, fill:#2F2440; 63 | classDef cluster color:#555, stroke:#BFD7ED, fill:#ffffff00; 64 | classDef noSpec color:#fff, fill:#BA0F30; 65 | classDef sunset color:#555, fill:#C6B79B; 66 | ``` 67 | 68 | -------------------------------------------------------------------------------- /content/message_types/post.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Post messages 3 | ... 4 | 5 | # `post` message 6 | 7 | Post messages are text-based messages intended for a public or private audience. They are what you normally see on feeds from social networks and make most of the meaningful interaction in Secure Scuttlebutt at the moment. 8 | 9 | ## What does it look like? 10 | 11 | ```{.json} 12 | { 13 | "key": "%gZIjLirxZKxCMmcsoud/bC0pL68UeB+Yuz+4re67TNk=.sha256", 14 | "value": { 15 | "previous": "%X8PLQuBhdUA+WF5VANRfG5iAKMNAeBXxlAtd9SKDAtM=.sha256", 16 | "sequence": 251, 17 | "author": "@qv10rF4IsmxRZb7g5ekJ33EakYBpdrmV/vtP1ij5BS4=.ed25519", 18 | "timestamp": 1560279840471, 19 | "hash": "sha256", 20 | "content": { 21 | "type": "post", 22 | "text": "YES WE CAN! :heart: :smiley_cat:", 23 | "root": "%Yx6/snCfur1NHd9fov8H359DfqTyncxuh93uZKnLQI8=.sha256", 24 | "branch": "%X8PLQuBhdUA+WF5VANRfG5iAKMNAeBXxlAtd9SKDAtM=.sha256", 25 | "channel": "patchfox" 26 | }, 27 | "signature": "nP2guRVtAJrvJpmcwG/K+mn4JgNANkK19+aiw+Y/Dn6axBBDB3sGfXKq/cXvrzurW1TB1yszzZebDrk3j+UBBA==.sig.ed25519" 28 | }, 29 | "timestamp": 1560279840471.001, 30 | "rts": 1560279840471 31 | } 32 | ``` 33 | 34 | These messages have a lot of fields. Lets go through the most important ones: 35 | 36 | * `text`: This is a Markdown-based textual content that is the body of your message. The other users will see a rendered version of this content. 37 | * `root`: In case this message is part of a thread of messages. The `root` fields points at the topmost message. 38 | * `branch`: If this message is a reply to another message, then `branch` points at the message this message is replying to. 39 | * `channel`: The channel this message is being posted to. It works much like hashtags but don't need to be present in the content itself. 40 | 41 | There are other optional fields such as: 42 | 43 | * `recps`: which holds the recipients for the messages and is used by private messages 44 | * `mentions`: which is used to help link mentioned users, blobs, and other messages. 45 | -------------------------------------------------------------------------------- /content/message_types/private.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Private messages 3 | ... 4 | 5 | # Private messages 6 | 7 | Not really a message type, but a different method of saving a message in the SSB ecosystem. 8 | 9 | One of the cool things about Secure Scuttlebutt is that it takes privacy seriously. The [cryptography techniques](https://ssbc.github.io/scuttlebutt-protocol-guide/#private-messages) employed by the SSB stack allows a group of people to message each other using encrypted text that is only visible to the involved parties, everyone else will just see undecypherable text. 10 | 11 | ## How does it looks like? 12 | 13 | ```{.json} 14 | { 15 | "key": "%OEZvP6IBUvadgsgCW16wN44TTMu0tntgS2YjOadCSLE=.sha256", 16 | "value": { 17 | "previous": "%YQJ+N13OCg14ixpsgsgdjrhe4t/lCI9+6P6oYPtZM=.sha256", 18 | "author": "@zurF8X68ArfRM71sdgsgW0xDM8QmOnAS5bYOq8hA=.ed25519", 19 | "sequence": 2529, 20 | "timestamp": 1560619975563, 21 | "hash": "sha256", 22 | "content": "DHczz4HPS1sdgsdg2bTMVXUggdA7wLopexXlNgQA2JukhCH8WKFNAu81fLcu0euPM/lTGuqxQBcAXoYIda6qKCGUwvxdgsgX6cCF3Ktu/BXIsYYk8TECYFM39gTfUTp0ORa18lussdgsdgZsCcOrHN4HJ7QF1dHIEgkWglJtMO+4Q4NwufiQ1keDqgs+xnE6h5qKLBcZjQ8d/uvhSEwhoyJ6oBFAw+MUn0NeF9UYWi2r0EKtMucditltqG1CsdgsdgasH2rGdt6GZkm89L7JahsYiZFtDPV1AQkQNhFp8cO5bHFmMQyhXFZBs0ABDqrq7l9skJlCKvU+w+1+ycDanwqhIoiAjU8+51JtZD4fWh81K/wkKOWBVZeYqZnimNsm9jX+AWlADGUe2GmS3yn/WRJfvcxgjGyGzzy5Sm7xU2XVol6WfKoMrLpedtl9CQ3uk7gR7WLmRUI78CkcJLHXDC3td4JSPWKPpCoh60pmdiG6e8e5aaYpM+LKeHNzqecXpcNKAh3r4281D/dma7lqbU7zujf5xIMni1VmUklVk6+KZr1CIwZUw+esP5DDns/Nyb/w8X1JwuVpSWm/lgsxqsQ==.box", 23 | "signature": "G6YHYOsdgsdgsdgqrgb7kANjZyegf5ZWM0B6kaQr/GjQ7ormNeOVAdw4jemYqSmW6GXezaR1UC70OymEvBBA==.sig.ed25519" 24 | }, 25 | "timestamp": 1560619916779, 26 | "rts": 1560619916779 27 | } 28 | ``` 29 | 30 | As you can see from the message above, the `content` is encrypted. Only the intended recipients for that message are able to decrypt it. All the other users will fail to decrypt it and ignore the message. 31 | 32 | Once decrypted by one of the intentend recipients, the message will be just like the other messages on SSB. -------------------------------------------------------------------------------- /content/message_types/more-info.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: More Info messages 3 | ... 4 | 5 | # `more-info` message 6 | 7 | Messages of type `more-info` are **PRIVATE** messages sent from one feed to itself. They attach additional metadata to a user. They are used to provide _address book_ features to SSB. Only the issuer of a `more-info` message can read them, so the data attached to a profile is private. 8 | 9 | ## What they look like 10 | 11 | ```{.json} 12 | { 13 | "key": "%buRChnotexistk/WjZQsfsgsg17XHmSDnMMKfFEXv/bjL43lQ=.sha256", 14 | "value": { 15 | "previous": null, 16 | "sequence": 1, 17 | "author": "@G/zUdqlPsdgsgd8yXIfMjx1676ApAOghwgc=.ed25519", 18 | "timestamp": 1560287825423, 19 | "hash": "sha256", 20 | "content": { 21 | "type": "more-info", 22 | "about": "@G/zUdqlPMsdgsdg8yXIfMjx1676ApAOghwgc=.ed25519", 23 | "luckyNumbers": ["1","2","3"], 24 | "fields": [ 25 | {"name": "primary email", "type": "email", "value": "example@example.com"}, 26 | {"name": "blog", "type": "URL", "value": "https://example.com"} 27 | ] 28 | }, 29 | "signature": "UMjf4aFsdgsgUY9zeDAqWdTZeymoQznicvfgATu0/aaaa==.sig.ed25519" 30 | }, 31 | "timestamp": 1560288248693, 32 | "rts": 1560287825423 33 | } 34 | 35 | ``` 36 | 37 | * `about`: This field should contain a _feed id_. 38 | * `luckyNumbers`: An array of numbers that is used to beef-up the cryptographic safety of the message. They provide some randomness to the data so that bad actors who know part of the other fields can't brute force and discover the keys more easily. 39 | * `fields`: An array of objects containing extra data about the user specified in the `about` field. 40 | * Each object in this array has a `label` that is displayed in the application UI, a `type` which dictates what kind of data it is holding, and a `value` which holds the data. 41 | 42 | The `more-info` message contains all the _more information_ about a given user. Applications when editing or updating that information send a complete final snapshot of all `fields`. An application should only read the last `more-info` message about a user and ignore any previous one (they should be treated as older versions). There is no reducing or tangles requiring a merge to make sense of the information. -------------------------------------------------------------------------------- /content/message_types/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Message Types 3 | ... 4 | 5 | 6 | # Message Types 7 | 8 | A [Scuttlebutt feed](https://ssbc.github.io/scuttlebutt-protocol-guide/#feeds) is a list of all the messages posted by a particular identity. When a user writes a message in a Scuttlebutt client and posts it, that message is put onto the end of their feed. 9 | 10 | Each [message](https://ssbc.github.io/scuttlebutt-protocol-guide/#message-format) has its own _type_ which identifies what kind of message it is. There are messages related to your social graph, private messages, chess playing messages, etc. People can define their own message types provided they follow [the message format mentioned above](https://ssbc.github.io/scuttlebutt-protocol-guide/#message-format). 11 | 12 | ## Custom Types 13 | 14 | Message schemas are interpretted according to the `type` property, as demonstrated in the other pages of this section. 15 | 16 | There is no restriction on which types applications use. A type simply must be a string between 3 and 52 characters long. You are free to create new types, with their own schemas, as you need them. 17 | 18 | Likewise, there is no restriction on message schemas, so long as the content is an object, and the total message size (including headers) is less than 8kb. 19 | 20 | ## Interoperation 21 | 22 | Applications should interpret messages "defensively." There's nothing enforcing a schema, so (as with any input) applications must be prepared for malformed content objects in messages. 23 | 24 | Applications should endeavor to interpret messages the same way. Otherwise, they won't be able to interoperate, and may introduce unexpected behaviors. 25 | 26 | There is no official mechanism for making sure message-types interoperate, except for the documentation which you're reading here. As it becomes clear that new types are coming into common use, we'll add them to this site. 27 | 28 | 29 | * [about](/message_types/about) 30 | * [blog](/message_types/blog) 31 | * [bookclub](/message_types/bookclub) 32 | * [bookclubUpdate](/message_types/bookclubUpdate) 33 | * [channel](/message_types/channel) 34 | * [contact](/message_types/contact) 35 | * [gathering](/message_types/gathering) 36 | * [more-info](/message_types/more-info) 37 | * [post](/message_types/post) 38 | * [private](/message_types/private) 39 | * [pub](/message_types/pub) 40 | * [vote](/message_types/vote) 41 | -------------------------------------------------------------------------------- /content/message_types/bookclubUpdate.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Book Club Update messages 3 | ... 4 | 5 | # `bookclubUpdate` message 6 | 7 | `bookclubUpdate` messages are used to update the metadata of an existing book. 8 | 9 | A book club update message looks like: 10 | 11 | ```{.json} 12 | { 13 | "type": "bookclubUpdate", 14 | "updates": "%OXfqGCrk4hvCwn6ReJJTFzeURlGRWO2KXJEv4M4282M=.sha256", 15 | "title": "Bullshit Jobs: A Theory", 16 | "description": "\"Back in 1930, the economist John Maynard Keynes prophesied that by the century's end, technology would see us all working fifteen-hour weeks. But instead, something curious happened. Today, average working hours have not decreased, but increased. And now, across the developed world, three-quarters of all jobs are in services or admin, jobs that don't seem to add anything to society: bullshit jobs. In Bullshit Jobs, David Graeber explores how this phenomenon - one more associated with the 20th-century Soviet Union, but which capitalism was supposed to eliminate - has happened. In doing so, he looks at how we value work, and how, rather than being productive, work has become an end in itself; the way such work maintains the current broken system of finance capital; and, finally, how we can get out of it.\"\n\nSource: https://www.amazon.co.uk/Bullshit-Jobs-Theory-David-Graeber/dp/0241263883", 17 | "authors": "David Graeber", 18 | "series": "", 19 | "seriesNo": "", 20 | "images": { 21 | "link": "&fYQo6nORDhDv0+NdxTYtv2vKDVPOtjByE6GMjBxY1h4=.sha256", 22 | "name": "41KYTsS8LpL._SX321_BO1,204,203,200_.jpg", 23 | "size": 21661, 24 | "type": "image/jpeg" 25 | }, 26 | "genres": "", 27 | "pages": "285" 28 | } 29 | 30 | ``` 31 | 32 | Be aware that an earlier version of the book club implementation used [`about` messages](/message_types/about) to update books. This is documented in [V1 schema of ssb-book-schema](https://github.com/ssbc/ssb-book-schema/). Most clients use the second version of that scheme and will not use `about` messages to update books. Those applications should still be able to read those old `about` messages though, even if they are using a different kind of message to write that information. The `scuttle-book` JS module will transparently take care of all that. More information at their git repository: [scuttle-book](https://github.com/ssbc/scuttle-book). 33 | 34 | You can read more about book club schemas in the [scuttle-book documentation](https://github.com/ssbc/ssb-book-schema/). 35 | -------------------------------------------------------------------------------- /content/message_types/blog.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Blog messages 3 | ... 4 | 5 | # `blog` messages 6 | 7 | Sometimes messages of type `post` are not enough for you to express what you need. Posts in Secure Scuttlebutt have a maximum size of 8k which is counting the metadata associated with the message and not just the content itself. For occasions when you need to write longer messages, you can use `blog`. 8 | 9 | These messages are actually a small set of metadata, just enough so that clients can display a summary and an associated [blob (this link explains them in depth)](/protocol/blobs) which holds the content. 10 | 11 | > **About blobs:** You can think of blobs like attachments to an email. Blobs are not downloaded automatically, they need to be requested. Most clients will request images on their own so that you can see posts and images together, but they will not request other blobs unless the user initiates some action that needs them. In cases such as `blog` messages, the blobs are requested when the user initiates the action to read the post. 12 | 13 | ## What do blog posts look like? 14 | 15 | ```{.json} 16 | { 17 | "key": "%x4+H95OSsvVgsPl6sggsfgsD0KPM8Y3eHQ9lj2oKNb4=.sha256", 18 | "value": { 19 | "previous": "%CwJcRGnZsdgsdg59NyL9gBokQzOzAoJJFjBE=.sha256", 20 | "sequence": 176, 21 | "author": "@aSo64imXSBLArsdgsdgidEUsvp2Lziiu3youY=.ed25519", 22 | "timestamp": 1560521419974, 23 | "hash": "sha256", 24 | "content": { 25 | "type": "blog", 26 | "title": "Fridays for Future treffen Science Fiction: Was ist eigentlich Solarpunk?", 27 | "summary": "Die Welt der Science Fiction ist bunt und vielfältig. Das zeigen Untergenres wie Cyberpunk und Steampunk – die mittlerweile weithin bekannt sind. Solarpunk ist hingegen nur wenigen ein Begriff. Dabei passt dieses Genre so gut in unsere Zeit wie kein anderes. Denn Solarpunk zeichnet eine Welt, in der wir unseren Planeten noch retten können.", 28 | "thumbnail": "&EmYpby5uFsdgsdg81wwggzPT52zZLNpQSoZu8=.sha256", 29 | "blog": "&DcG4eJNU65yuVMjwNIsdgsdgsdZfmTIfXQrTxOQ=.sha256", 30 | "mentions": [ 31 | { 32 | "link": "&pU8sdgsdgsSIs3z5kw25OPoqmDbwLoNPIJE6XI=.sha256", 33 | "type": "image/jpeg", 34 | "size": 195993 35 | } 36 | ], 37 | "channel": "solarpunk" 38 | }, 39 | "signature": "51SNzLdPRDMWNVTdBjU0TplV9gbmyehpcWKsCz9DsWlz0gFynzybXMJozMUC5GieNPUAaMnob9YFe4sH6nMjAA==.sig.ed25519" 40 | } 41 | } 42 | ``` 43 | 44 | On the message above you can see some metadata to helps the application display a summary of the blog post to the user using the fields `summary`, `title`, `thumbnail`. The content for the blogpost is a blob in field `blog`. Blobs start with an `%`. Clients will fetch that blob and display it if once the user ask to read the post. The content of the blob should be a markdown text file. 45 | -------------------------------------------------------------------------------- /docs/protocol/img/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 45 | 48 | 49 | 51 | 52 | 54 | image/svg+xml 55 | 57 | 58 | 59 | 60 | 61 | 66 | 72 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /content/protocol/img/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 45 | 48 | 49 | 51 | 52 | 54 | image/svg+xml 55 | 57 | 58 | 59 | 60 | 61 | 66 | 72 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /.github/workflows/build_documentation.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the "main" branch 8 | push: 9 | branches: [ "main" ] 10 | pull_request: 11 | branches: [ "main" ] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | env: 17 | #CHROME_BIN: "/usr/bin/chromium-browser" 18 | PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: "true" 19 | 20 | 21 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 22 | jobs: 23 | # This workflow contains a single job called "build" 24 | build: 25 | # The type of runner that the job will run on 26 | runs-on: ubuntu-22.04 27 | 28 | # Steps represent a sequence of tasks that will be executed as part of the job 29 | steps: 30 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 31 | - uses: actions/checkout@v3 32 | with: 33 | submodules: "recursive" 34 | - uses: browser-actions/setup-chrome@latest 35 | 36 | # Runs a set of commands using the runners shell 37 | - name: Install dependencies 38 | run: | 39 | sudo apt-get update 40 | sudo apt-get install -y build-essential libreadline-dev unzip 41 | sudo apt-get install -y pandoc texlive-latex-base texlive-fonts-recommended texlive-extra-utils texlive-latex-extra texlive-xetex 42 | sudo apt-get install -y imagemagick 43 | sudo apt-get install -y nodejs npm 44 | sudo apt-get install -y lua5.3 liblua5.3-dev 45 | sudo update-alternatives --install /usr/bin/lua lua-interpreter /usr/bin/lua5.3 130 46 | sudo update-alternatives --install /usr/bin/luac lua-compiler /usr/bin/luac5.3 130 47 | sudo apt-get install -y luarocks 48 | sudo luarocks install luafilesystem 49 | sudo luarocks install lua-toml 50 | sudo luarocks install f-strings 51 | sudo luarocks install penlight 52 | sudo npm install --global mermaid-filter 53 | shell: bash 54 | - name: Build HTML 55 | run: | 56 | lua ./scripts/lua/build.lua --html 57 | shell: bash 58 | - name: Setup Pages 59 | uses: actions/configure-pages@v2 60 | - name: Upload artifact 61 | uses: actions/upload-pages-artifact@v1 62 | with: 63 | # Upload entire ./docs 64 | path: './docs' 65 | deploy: 66 | # The type of runner that the job will run on 67 | runs-on: ubuntu-22.04 68 | needs: build 69 | 70 | permissions: 71 | contents: read 72 | pages: write # to deploy to Pages 73 | id-token: write # to verify the deployment originates from an appropriate source 74 | deployments: write 75 | environment: 76 | name: github-pages 77 | url: ${{ steps.deployment.outputs.page_url }} 78 | 79 | # Steps represent a sequence of tasks that will be executed as part of the job 80 | steps: 81 | - name: Deploy to GitHub Pages 82 | id: deployment 83 | uses: actions/deploy-pages@v1 -------------------------------------------------------------------------------- /content/protocol/discovery.md: -------------------------------------------------------------------------------- 1 | # Discovery 2 | 3 | After a user has generated their identity they need to find some peers to connect to. To connect to a peer you need its public key and its address using any of the protocol it supports. Typically with TCP/IP, you would need its IP address and port, but the Scuttlebutt protocol is not restricted to TCP/IP as transport. The Scuttlebutt protocol currently has three methods for peers to discover each other. 4 | 5 | ## Local network 6 | 7 | Peers constantly broadcast UDP packets on their local network advertising their presence. The body of each packet is a string containing the peer’s IP address, port and base64-encoded public key (without `@` or `.ed25519`): 8 | 9 | > ### ![](img/impl16.png) Implementations 10 | > 11 | > **JS:** 12 | > 13 | > * [broadcast-stream](https://github.com/dominictarr/broadcast-stream/blob/master/index.js) 14 | > * [ssb-local](https://github.com/ssbc/ssb-local/blob/master/index.js) 15 | > 16 | > **Java:** 17 | > 18 | > * [LocalDiscoveryService](https://github.com/apache/incubator-tuweni/blob/master/scuttlebutt-discovery/src/main/java/org/apache/tuweni/scuttlebutt/discovery/ScuttlebuttLocalDiscoveryService.java) 19 | 20 | 21 | | Source IP | Source port | Destination IP | Destination port | 22 | |----|----|----|---- | 23 | | 192.168.1.123 | 8008 | 255.255.255.255 | 8008 | 24 | 25 | ![net:192.168.1.123:8008:~shs:FCX/tsDLpubCPKKfIrw4gc+SQkHcaD17s7GI6i/ziWY=](img/format_udp_broadcast.png) 26 | 27 | This message format can be any valid [multiserver address](https://github.com/ssb-js/multiserver#address-format). In local networks, this is usually a `net` address with an IPv4 or IPv6 address. 28 | 29 | Current implementations broadcast one of these packets every second. When a peer sees another peer’s broadcast packet they can connect to exchange messages. Some clients show nearby peers in the user interface so that the user can see other people on the same network as them. 30 | 31 | UDP source and destination ports are set to the same port number that the peer is listening on TCP for peer connections (normally 8008). 32 | 33 | ## Invite code 34 | 35 | [Invite codes](#invites) help new users get connected to their first [pub](#pubs), which is a Scuttlebutt peer that is publicly accessible over the internet. An invite code contains a pub’s domain name, port and public key. 36 | 37 | They also contain a secret key that the user can [redeem](#redeeming-invites) to make the pub [follow](#following) them back. This lets the new user see messages posted by other members of the pub and share their own messages. Invite codes are the most common way for new users to get started on Scuttlebutt. 38 | 39 | Pub operators can distribute invite codes any way they see fit, for example by posting them on existing social networks. Some pubs have a web page that anybody can visit to generate an invite code. 40 | 41 | ## Pub message 42 | 43 | Users can post a message to their own [feed](#feeds) advertising a pub: 44 | 45 | Here the user `@FCX/ts…` is advertising that they know of pub `@VJM7w1…` along with the pub’s domain name and port. 46 | 47 | { 48 | "author": "@FCX/tsDLpubCPKKfIrw4gc+SQkHcaD17s7GI6i/ziWY=.ed25519", 49 | "content": { 50 | "type": "pub", 51 | "address": { 52 | "host": "one.butt.nz", 53 | "port": 8008, 54 | "key": "@VJM7w1W19ZsKmG2KnfaoKIM66BRoreEkzaVm/J//wl8=.ed25519" 55 | } 56 | }, 57 | … 58 | } 59 | 60 | When others see this message they can make a note that this pub exists and connect to it in the future. 61 | 62 | Pub messages are a useful way to find additional peers if you already know a few. Obviously this doesn’t work for new users who don’t know anyone else yet and therefore can’t see any pub messages. -------------------------------------------------------------------------------- /content/message_types/about.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: About messages 3 | ... 4 | 5 | # `about` messages 6 | 7 | `about` messages are very flexible. They are often used to add or alter metadata _about_ some other message or feed. 8 | 9 | ## about (gathering) 10 | 11 | In the case of a [gathering](/message_types/gathering), the message will be used to attach metadata such as description and date, but also to flag people as attending the event. Let's look at some samples: 12 | 13 | ```{.json} 14 | { 15 | "author": "@GF214rVSdwf/nARy7oDh+AxJViVCGfKqw+aa162itZY=.ed25519", 16 | "content": { 17 | "about": "%1T2IbTOXd7euvhEsHnZ9WgsRRNUgqfXVbLW/w4Hueak=.sha256", 18 | "startDateTime": { 19 | "bias": -60, 20 | "epoch": 1514113200000, 21 | "tz": "Canada/Pacific", 22 | "valid": true 23 | }, 24 | "title": "Christmas", 25 | "type": "about" 26 | }, 27 | "hash": "sha256", 28 | "previous": "%1T2IbTOXd7euvhEsHnZ9WgsRRNUgqfXVbLW/w4Hueak=.sha256", 29 | "sequence": 15, 30 | "signature": "K2SL5Y3UpO1NjSwNCY8+dSOeEWjhgD/65b2hzacuR0d+lPdW8dY63/2zgxax9XD5UDzjwfPRM9ogNHcmI1ldCw==.sig.ed25519", 31 | "timestamp": 1513623739160 32 | } 33 | 34 | ``` 35 | 36 | The message sample above is setting the _date_ for a [gathering](/message_types/gathering) and adding a _title_ to it. 37 | 38 | Let's look at another case of `about` being used with _gatherings_: 39 | 40 | ```{.json} 41 | { 42 | "author": "@GF214rVSdwf/nARy7oDh+AxJViVCGfKqw+aa162itZY=.ed25519", 43 | "content": { 44 | "about": "%1T2IbTOXd7euvhEsHnZ9WgsRRNUgqfXVbLW/w4Hueak=.sha256", 45 | "attendee": { 46 | "link": "@GF214rVSdwf/nARy7oDh+AxJViVCGfKqw+aa162itZY=.ed25519" 47 | }, 48 | "type": "about" 49 | }, 50 | "hash": "sha256", 51 | "previous": "%6AS8z2tf+qS0HYxf/pH4KGK7m0/NCDujnY6ATwDX5Vg=.sha256", 52 | "sequence": 16, 53 | "signature": "CyZ6CT9shZmiajt3UbYrP0WyjaHf3EdALzQQa264x9QdzZxVe/YyQk5Pfz+/uApHHtisf5Lv+6VjQrCRzUDZCw==.sig.ed25519", 54 | "timestamp": 1513623753905 55 | } 56 | 57 | ``` 58 | 59 | That message is marking a _feed_ (aka a user) as attending the _gathering_. 60 | 61 | 62 | ## about (profile update) 63 | 64 | About messages are often used to add additional information to other messages, much like attaching post-it notes to some book page. You're _attaching information about_ something. The most common use of `about` messages is to add names, images and descriptions to a user profile. 65 | 66 | > **Attention:** There is no central authority handling names on the Scuttleverse. Much like in the physical world where two people can have the exact same name so it is on Scuttlebutt. 67 | > 68 | > You can name other people as well, just like partners and friends do although application are not forced to respect third-party names. 69 | 70 | 71 | ## What does it look like? 72 | 73 | ```{.json} 74 | { 75 | "key": "%buRChtQMrk/WjZQsfsgsg17XHmSDnMMKfFEXv/bjL43lQ=.sha256", 76 | "value": { 77 | "previous": null, 78 | "sequence": 1, 79 | "author": "@G/zUdqlPsdgsgd8yXIfMjx1676ApAOghwgc=.ed25519", 80 | "timestamp": 1560287825423, 81 | "hash": "sha256", 82 | "content": { 83 | "type": "about", 84 | "about": "@G/zUdqlPMsdgsdg8yXIfMjx1676ApAOghwgc=.ed25519", 85 | "image": "&N3ectV2qM5gyH2Zrsdgsdgd+InCMkJBc/MaTbJ0=.sha256", 86 | "name": "Hilo", 87 | "description": "extrem klug" 88 | }, 89 | "signature": "UMjf4aFsdgsgUY9zeDAqWdTZeymoQznicvfgATu0/kArvLnshqbkiG7ZIngXcnztMUc6SyI4GrDwkAA==.sig.ed25519" 90 | }, 91 | "timestamp": 1560288248693, 92 | "rts": 1560287825423 93 | } 94 | ``` 95 | 96 | The message above is adding a `name`, an `image` and a `description` to the user specified by the `about` field. -------------------------------------------------------------------------------- /content/protocol/cryptography.md: -------------------------------------------------------------------------------- 1 | # Cryptography 2 | 3 | ## Keys and identities 4 | 5 | The first thing a user needs to participate in Scuttlebutt is an identity. An identity is an Ed25519 key pair and typically represents a person, a device, a server or a bot. It’s normal for a person to have several Scuttlebutt identities. 6 | 7 | Upon starting for the first time, Scuttlebutt clients will automatically generate an Ed25519 key pair and save it in the user’s home folder under `.ssb/secret`. 8 | 9 | ![The Scuttlebutt identity is a long-term Ed25519 key pair.](img/identity_keypair.png) 10 | 11 | Because identities are long and random, no coordination or permission is required to create a new one, which is essential to the network’s design. 12 | 13 | Later, a user can choose to give themselves a nickname or avatar to make themselves easier to refer to. Over time nicknames may change but identities stay the same. If a user loses their secret key or has it stolen they will need to generate a new identity and tell people to use their new one instead. 14 | 15 | The public key of an identity is presented to users and transmitted in some parts of the network protocol using this format: 16 | 17 | ![@FCX/tsDLpubCPKKfIrw4gc+SQkHcaD17s7GI6i/ziWY=.ed25519 where everything but the @ prefix and .ed25519 suffix is the public-key, base64-encoded.](img/format_public_key.png) 18 | 19 | Throughout the protocol all instances of base64 are the variant that uses `+` and `/`. The final padding `=` is also required. 20 | 21 | The beginning `@` sign signifies that this represents a public key rather than a message or blob, which start with `%` and `&`. Each identity has an associated [feed](#feeds), which is a list of all the messages posted by that identity. This is why the identity is also called a _feed ID_. 22 | 23 | ## Cryptographic primitives 24 | 25 | The Scuttlebutt protocol relies on NaCl/libsodium's cryptobox primitives. This guide uses the following: 26 | 27 | `nacl_scalarmult(n, p)` 28 | 29 | This is [Libsodium's scalar multiplication function](https://doc.libsodium.org/advanced/scalar_multiplication), which takes two scalars (usually public and/or secret keys). It has the useful property that, given two key pairs `(pk1, sk1)` and `(pk2, sk2)`, `nacl_scalarmult(sk1, pk2) == nacl_scalarmult(sk2, pk1)`, which allows shared secret derivation between peers who know each other's public key. More on this later. 30 | 31 | `nacl_auth(msg, key)` and `assert_nacl_auth_verify(authenticator, msg, key)` 32 | 33 | This functions are [Libsodium's message authentication function](https://doc.libsodium.org/public-key_cryptography/authenticated_encryption). The former takes a message and returns a 32-bytes authenticator, that acts as a detacted signature of the message. The latter verifies this authenticator is indeed valid for the given message and key; and errors if they don't. 34 | 35 | `nacl_secret_box(msg, nonce, key)` and `assert_nacl_secretbox_open(ciphertext, nonce, key)` 36 | 37 | These function are based on [Libsodium's crypto\_secretbox\_easy and crypto\_secretbox\_open\_easy function](https://doc.libsodium.org/secret-key_cryptography/secretbox), which use symmetric cryptography to, respectively, encrypt+authenticate, and verify+decrypt a message using a nonce and a shared secret. 38 | 39 | `nacl_sign_detached(msg, key)` and `assert_nacl_sign_verify_detached(sig, msg, key)` 40 | 41 | The former is computed from [Libsodium's signature functions](https://doc.libsodium.org/public-key_cryptography/public-key_signatures). Unlike the usual Libsodium/NaCl functions, they work with signatures in independent buffers, rather than concatenated with the msg. 42 | 43 | `pk_to_curve25519(ed25519_pk)` and `sk_to_curve25519(ed25519_sk)` 44 | 45 | These functions convert Ed25519 keys (used for cryptobox) to Curve25519 (aka X25519) keys, used for signing. They are [implemented by Libsodium as `crypto_sign_ed25519_pk_to_curve25519` and `crypto_sign_ed25519_sk_to_curve25519`](https://doc.libsodium.org/advanced/ed25519-curve25519), respectively. -------------------------------------------------------------------------------- /docs/protocol/img/room_invite_uri.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ssb 8 | : 9 | experimental 10 | ?action=claim-http-invite&invite= 11 | 39c0ac1850ec9af14f1bb73 12 | 13 | 14 | &postTo= 15 | https%3A%2F%2Fscuttlebutt.eu%2Fclaiminvite 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Submission URL 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | invite code 38 | 39 | 40 | 41 | 42 | 43 | type of 44 | SSB URI 45 | 46 | 47 | 48 | 49 | 50 | 51 | protocol 52 | schema 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /content/protocol/img/room_invite_uri.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ssb 8 | : 9 | experimental 10 | ?action=claim-http-invite&invite= 11 | 39c0ac1850ec9af14f1bb73 12 | 13 | 14 | &postTo= 15 | https%3A%2F%2Fscuttlebutt.eu%2Fclaiminvite 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Submission URL 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | invite code 38 | 39 | 40 | 41 | 42 | 43 | type of 44 | SSB URI 45 | 46 | 47 | 48 | 49 | 50 | 51 | protocol 52 | schema 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /docs/simple.css: -------------------------------------------------------------------------------- 1 | /* minireset */ 2 | @import url("https://cdn.jsdelivr.net/gh/jgthms/minireset.css@master/minireset.min.css"); 3 | 4 | body { 5 | font-family: Lato,proxima-nova,Helvetica Neue,Arial,sans-serif; 6 | font-weight: 400; 7 | color: #404040; 8 | } 9 | 10 | .container { 11 | display: flex; 12 | } 13 | 14 | .logo { 15 | padding: 10px; 16 | text-align: center; 17 | } 18 | 19 | .toc-title { 20 | color: black; 21 | font-size: 1.3rem; 22 | padding-left: 20px; 23 | padding-top: 10px; 24 | } 25 | 26 | aside { 27 | flex: 1 1 auto; 28 | position: fixed; 29 | top: 0; 30 | bottom: 0; 31 | left: 0; 32 | padding-bottom: 2em; 33 | width: 300px; 34 | overflow-x: hidden; 35 | overflow-y: hidden; 36 | min-height: 100%; 37 | color: #9b9b9b; 38 | background: #BFCDF3; 39 | z-index: 200; 40 | display: flex; 41 | flex-direction: column; 42 | } 43 | 44 | aside nav { 45 | width: 300px; 46 | position: relative; 47 | overflow-x: hidden; 48 | overflow-y: overlay; 49 | height: 100%; 50 | padding-top: 20px; 51 | padding-bottom: 20px; 52 | } 53 | 54 | aside nav ul { 55 | padding: 0px; 56 | margin: 0px; 57 | } 58 | 59 | aside nav li { 60 | color: color: #9b9b9b; 61 | list-style: none; 62 | } 63 | 64 | aside nav li a { 65 | line-height: 18px; 66 | padding-left: 20px; 67 | padding-top: 10px; 68 | display: block; 69 | position: relative; 70 | font-size: 90%; 71 | color: #black; 72 | text-decoration: none; 73 | cursor: pointer; 74 | } 75 | 76 | aside nav li ul { 77 | padding-left: 20px; 78 | } 79 | 80 | aside nav li a:hover { 81 | background-color: violet; 82 | cursor: pointer; 83 | } 84 | 85 | aside nav li a:hover { 86 | text-decoration: none; 87 | } 88 | 89 | .aside-footer { 90 | text-align: center; 91 | height: 20px; 92 | } 93 | 94 | article { 95 | flex: 1 1 auto; 96 | margin-left: 300px; 97 | padding: 2rem; 98 | overflow-y: hidden; 99 | } 100 | 101 | img { 102 | max-width: 80%; 103 | } 104 | 105 | /* fix for scrollbar in webkit */ 106 | 107 | 108 | ::-webkit-scrollbar { 109 | width: 10px; 110 | height: 10px; 111 | } 112 | 113 | ::-webkit-scrollbar-thumb { 114 | background: rgba(90, 90, 90); 115 | } 116 | 117 | ::-webkit-scrollbar-track { 118 | background: rgba(0, 0, 0, 0.2); 119 | } 120 | 121 | /* Typography */ 122 | 123 | h1 { 124 | font-size: 2.5em; 125 | } 126 | 127 | h2 { 128 | font-size: 1.5em; 129 | } 130 | 131 | h3 { 132 | font-size: 1.2em; 133 | } 134 | 135 | h1, h2, h3, h4, h5, h6 { 136 | margin: 1.25em 0 0; 137 | line-height: 1.2; 138 | } 139 | 140 | figure + p, h1 + details, h1 + p, h2 + details, h2 + p, h3 + details, h3 + p, h4 + details, h4 + p, h5 + details, h5 + p, h6 + details, h6 + p { 141 | margin-top: .5em; 142 | } 143 | 144 | figcaption { 145 | text-align: center; 146 | font-style: italic; 147 | } 148 | 149 | p { 150 | margin: 1em 0; 151 | margin-top: 1em; 152 | -webkit-hyphens: auto; 153 | -ms-hyphens: auto; 154 | hyphens: auto; 155 | } 156 | 157 | article img, picture { 158 | display: block; 159 | max-width: 100%; 160 | margin: 0 auto; 161 | } 162 | 163 | table { 164 | display: inline-block; 165 | border-spacing: 0; 166 | border-collapse: collapse; 167 | overflow-x: auto; 168 | max-width: 100%; 169 | text-align: left; 170 | vertical-align: top; 171 | background: linear-gradient(rgba(0,0,0,.15) 0%,rgba(0,0,0,.15) 100%) 0 0,linear-gradient(rgba(0,0,0,.15) 0%,rgba(0,0,0,.15) 100%) 100% 0; 172 | background-repeat: repeat, repeat; 173 | background-attachment: scroll, scroll; 174 | background-size: auto, auto; 175 | background-attachment: scroll,scroll; 176 | background-size: 1px 100%,1px 100%; 177 | background-repeat: no-repeat,no-repeat; 178 | } 179 | 180 | table td:first-child, table th:first-child { 181 | padding-left: 0; 182 | background-image: linear-gradient(to right,#fff 50%,rgba(255,255,255,0) 100%); 183 | background-size: 2px 100%; 184 | background-repeat: no-repeat; 185 | } 186 | 187 | table td, table th { 188 | padding: .35em .75em; 189 | padding-left: 0.75em; 190 | vertical-align: top; 191 | font-size: .9em; 192 | border: 1px solid #f2f2f2; 193 | border-top-color: rgb(242, 242, 242); 194 | border-top-style: solid; 195 | border-top-width: 1px; 196 | border-left-color: rgb(242, 242, 242); 197 | border-left-style: solid; 198 | border-left-width: 1px; 199 | border-top: 0; 200 | border-left: 0; 201 | } 202 | 203 | p + ol, p + ul { 204 | margin-top: -.75em; 205 | } 206 | ol, ul { 207 | list-style: unset; 208 | margin-top: 0; 209 | padding-top: 0; 210 | padding-left: 2.5em; 211 | } 212 | 213 | pre code.sourceCode { 214 | overflow-x: auto; 215 | white-space: pre-wrap; 216 | white-space: -moz-pre-wrap; 217 | white-space: -pre-wrap; 218 | white-space: -o-pre-wrap; 219 | word-wrap: break-word; 220 | } -------------------------------------------------------------------------------- /templates/simple/simple.css: -------------------------------------------------------------------------------- 1 | /* minireset */ 2 | @import url("https://cdn.jsdelivr.net/gh/jgthms/minireset.css@master/minireset.min.css"); 3 | 4 | body { 5 | font-family: Lato,proxima-nova,Helvetica Neue,Arial,sans-serif; 6 | font-weight: 400; 7 | color: #404040; 8 | } 9 | 10 | .container { 11 | display: flex; 12 | } 13 | 14 | .logo { 15 | padding: 10px; 16 | text-align: center; 17 | } 18 | 19 | .toc-title { 20 | color: black; 21 | font-size: 1.3rem; 22 | padding-left: 20px; 23 | padding-top: 10px; 24 | } 25 | 26 | aside { 27 | flex: 1 1 auto; 28 | position: fixed; 29 | top: 0; 30 | bottom: 0; 31 | left: 0; 32 | padding-bottom: 2em; 33 | width: 300px; 34 | overflow-x: hidden; 35 | overflow-y: hidden; 36 | min-height: 100%; 37 | color: #9b9b9b; 38 | background: #BFCDF3; 39 | z-index: 200; 40 | display: flex; 41 | flex-direction: column; 42 | } 43 | 44 | aside nav { 45 | width: 300px; 46 | position: relative; 47 | overflow-x: hidden; 48 | overflow-y: overlay; 49 | height: 100%; 50 | padding-top: 20px; 51 | padding-bottom: 20px; 52 | } 53 | 54 | aside nav ul { 55 | padding: 0px; 56 | margin: 0px; 57 | } 58 | 59 | aside nav li { 60 | color: #9b9b9b; 61 | list-style: none; 62 | } 63 | 64 | aside nav li a { 65 | line-height: 18px; 66 | padding-left: 20px; 67 | padding-top: 10px; 68 | display: block; 69 | position: relative; 70 | font-size: 90%; 71 | color: #000; 72 | text-decoration: none; 73 | cursor: pointer; 74 | } 75 | 76 | aside nav li a:visited { 77 | color: #000; 78 | } 79 | 80 | aside nav li ul { 81 | padding-left: 20px; 82 | } 83 | 84 | aside nav li a:hover { 85 | background-color: violet; 86 | cursor: pointer; 87 | } 88 | 89 | aside nav li a:hover { 90 | text-decoration: none; 91 | } 92 | 93 | .aside-footer { 94 | text-align: center; 95 | height: 20px; 96 | } 97 | 98 | article { 99 | flex: 1 1 auto; 100 | margin-left: 300px; 101 | padding: 2rem; 102 | overflow-y: hidden; 103 | } 104 | 105 | img { 106 | max-width: 80%; 107 | } 108 | 109 | /* fix for scrollbar in webkit */ 110 | 111 | 112 | ::-webkit-scrollbar { 113 | width: 10px; 114 | height: 10px; 115 | } 116 | 117 | ::-webkit-scrollbar-thumb { 118 | background: rgba(90, 90, 90); 119 | } 120 | 121 | ::-webkit-scrollbar-track { 122 | background: rgba(0, 0, 0, 0.2); 123 | } 124 | 125 | /* Typography */ 126 | 127 | h1 { 128 | font-size: 2.5em; 129 | } 130 | 131 | h2 { 132 | font-size: 1.5em; 133 | } 134 | 135 | h3 { 136 | font-size: 1.2em; 137 | } 138 | 139 | h1, h2, h3, h4, h5, h6 { 140 | margin: 1.25em 0 0; 141 | line-height: 1.2; 142 | } 143 | 144 | figure + p, h1 + details, h1 + p, h2 + details, h2 + p, h3 + details, h3 + p, h4 + details, h4 + p, h5 + details, h5 + p, h6 + details, h6 + p { 145 | margin-top: .5em; 146 | } 147 | 148 | figcaption { 149 | text-align: center; 150 | font-style: italic; 151 | } 152 | 153 | p { 154 | margin: 1em 0; 155 | margin-top: 1em; 156 | -webkit-hyphens: auto; 157 | -ms-hyphens: auto; 158 | hyphens: auto; 159 | } 160 | 161 | article img, picture { 162 | display: block; 163 | max-width: 100%; 164 | margin: 0 auto; 165 | } 166 | 167 | table { 168 | display: inline-block; 169 | border-spacing: 0; 170 | border-collapse: collapse; 171 | overflow-x: auto; 172 | max-width: 100%; 173 | text-align: left; 174 | vertical-align: top; 175 | background: linear-gradient(rgba(0,0,0,.15) 0%,rgba(0,0,0,.15) 100%) 0 0,linear-gradient(rgba(0,0,0,.15) 0%,rgba(0,0,0,.15) 100%) 100% 0; 176 | background-repeat: repeat, repeat; 177 | background-attachment: scroll, scroll; 178 | background-size: auto, auto; 179 | background-attachment: scroll,scroll; 180 | background-size: 1px 100%,1px 100%; 181 | background-repeat: no-repeat,no-repeat; 182 | } 183 | 184 | table td:first-child, table th:first-child { 185 | padding-left: 0; 186 | background-image: linear-gradient(to right,#fff 50%,rgba(255,255,255,0) 100%); 187 | background-size: 2px 100%; 188 | background-repeat: no-repeat; 189 | } 190 | 191 | table td, table th { 192 | padding: .35em .75em; 193 | padding-left: 0.75em; 194 | vertical-align: top; 195 | font-size: .9em; 196 | border: 1px solid #f2f2f2; 197 | border-top-color: rgb(242, 242, 242); 198 | border-top-style: solid; 199 | border-top-width: 1px; 200 | border-left-color: rgb(242, 242, 242); 201 | border-left-style: solid; 202 | border-left-width: 1px; 203 | border-top: 0; 204 | border-left: 0; 205 | } 206 | 207 | p + ol, p + ul { 208 | margin-top: -.75em; 209 | } 210 | ol, ul { 211 | list-style: unset; 212 | margin-top: 0; 213 | padding-top: 0; 214 | padding-left: 2.5em; 215 | } 216 | 217 | pre code.sourceCode { 218 | overflow-x: auto; 219 | white-space: pre-wrap; 220 | white-space: -moz-pre-wrap; 221 | white-space: -pre-wrap; 222 | white-space: -o-pre-wrap; 223 | word-wrap: break-word; 224 | } 225 | 226 | html { 227 | scroll-behavior: smooth; 228 | } 229 | 230 | :target:before { 231 | content: ""; 232 | display: block; 233 | height: 30px; 234 | margin: -30px 0 0; 235 | } 236 | 237 | 238 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Welcome to SSB Documentation 9 | 23 | 24 | 27 | 28 | 29 | 30 |
31 | 92 |
93 |

Secure Scuttlebutt

94 |

Welcome to the SSB documentation. At the moment this is closed for 95 | renovations. In the meanwhile check the work-in-progress sections:

96 | 100 |
101 |
102 | 103 | -------------------------------------------------------------------------------- /docs/protocol/introduction.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | introduction 9 | 23 | 24 | 27 | 28 | 29 | 30 |
31 | 92 |
93 |

Introduction

94 |

Scuttlebutt is a protocol 95 | for building decentralized applications that work well offline and that 96 | no one person can control. Because there is no central server, 97 | Scuttlebutt clients connect to their peers to exchange information. This 98 | guide describes the protocols used to communicate within the Scuttlebutt 99 | network.

100 |

Scuttlebutt is a flexible protocol, capable of supporting many 101 | different types of applications. One of its first applications was as a 102 | social network. This guide has a slight focus on how to use Scuttlebutt 103 | for social networking, but many of the explanations will still be useful 104 | if want to use it for something completely different, or are just 105 | curious how it works.

106 |
107 |
108 | 109 | -------------------------------------------------------------------------------- /content/protocol/img/format_public_key.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 50 | 53 | 54 | 56 | 57 | 59 | image/svg+xml 60 | 62 | 63 | 64 | 65 | 66 | 71 | 78 | @FCX/tsDLpubCPKKfIrw4gc+SQkHcaD17s7GI6i/ziWY=.ed25519 91 | 97 | base64(public key) 108 | 109 | -------------------------------------------------------------------------------- /docs/protocol/img/format_public_key.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 50 | 53 | 54 | 56 | 57 | 59 | image/svg+xml 60 | 62 | 63 | 64 | 65 | 66 | 71 | 78 | @FCX/tsDLpubCPKKfIrw4gc+SQkHcaD17s7GI6i/ziWY=.ed25519 91 | 97 | base64(public key) 108 | 109 | -------------------------------------------------------------------------------- /content/protocol/img/format_message_id.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 50 | 53 | 54 | 56 | 57 | 59 | image/svg+xml 60 | 62 | 63 | 64 | 65 | 66 | 71 | 78 | %R7lJEkz27lNijPhYNDzYoPjM0Fp+bFWzwX0SmNJB/ZE=.sha256 91 | 97 | base64(sha256(formatted message including signature)) 108 | 109 | -------------------------------------------------------------------------------- /docs/protocol/img/format_message_id.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 50 | 53 | 54 | 56 | 57 | 59 | image/svg+xml 60 | 62 | 63 | 64 | 65 | 66 | 71 | 78 | %R7lJEkz27lNijPhYNDzYoPjM0Fp+bFWzwX0SmNJB/ZE=.sha256 91 | 97 | base64(sha256(formatted message including signature)) 108 | 109 | -------------------------------------------------------------------------------- /docs/protocol/img/format_blob_id.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 50 | 53 | 54 | 56 | 57 | 59 | image/svg+xml 60 | 62 | 63 | 64 | 65 | 66 | 71 | 78 | &uaGieSQDJcHfUp6hjIcIq55GoZh4Ug7tNmgaohoxrpw=.sha256 93 | 99 | base64(sha256(blob)) 110 | 111 | -------------------------------------------------------------------------------- /content/protocol/img/format_blob_id.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 50 | 53 | 54 | 56 | 57 | 59 | image/svg+xml 60 | 62 | 63 | 64 | 65 | 66 | 71 | 78 | &uaGieSQDJcHfUp6hjIcIq55GoZh4Ug7tNmgaohoxrpw=.sha256 93 | 99 | base64(sha256(blob)) 110 | 111 | -------------------------------------------------------------------------------- /docs/message_types/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Message Types 9 | 23 | 24 | 27 | 28 | 29 | 30 |
31 | 92 |
93 |

Message Types

94 |

A Scuttlebutt 96 | feed is a list of all the messages posted by a particular identity. 97 | When a user writes a message in a Scuttlebutt client and posts it, that 98 | message is put onto the end of their feed.

99 |

Each message 101 | has its own type which identifies what kind of message it is. 102 | There are messages related to your social graph, private messages, chess 103 | playing messages, etc. People can define their own message types 104 | provided they follow the 106 | message format mentioned above.

107 |

Custom Types

108 |

Message schemas are interpretted according to the type 109 | property, as demonstrated in the other pages of this section.

110 |

There is no restriction on which types applications use. A type 111 | simply must be a string between 3 and 52 characters long. You are free 112 | to create new types, with their own schemas, as you need them.

113 |

Likewise, there is no restriction on message schemas, so long as the 114 | content is an object, and the total message size (including headers) is 115 | less than 8kb.

116 |

Interoperation

117 |

Applications should interpret messages “defensively.” There’s nothing 118 | enforcing a schema, so (as with any input) applications must be prepared 119 | for malformed content objects in messages.

120 |

Applications should endeavor to interpret messages the same way. 121 | Otherwise, they won’t be able to interoperate, and may introduce 122 | unexpected behaviors.

123 |

There is no official mechanism for making sure message-types 124 | interoperate, except for the documentation which you’re reading here. As 125 | it becomes clear that new types are coming into common use, we’ll add 126 | them to this site.

127 | 141 |
142 |
143 | 144 | -------------------------------------------------------------------------------- /content/protocol/img/format_udp_broadcast.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 50 | 53 | 54 | 56 | 57 | 59 | image/svg+xml 60 | 62 | 63 | 64 | 65 | 66 | 71 | net:192.168.1.123:8008~shs:FCX/tsDLpubCPKKfIrw4gc+SQkHcaD17s7GI6i/ziWY= 88 | 94 | base64(public key) 105 | 111 | 117 | port 128 | IP address 139 | 140 | -------------------------------------------------------------------------------- /docs/protocol/img/format_udp_broadcast.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 50 | 53 | 54 | 56 | 57 | 59 | image/svg+xml 60 | 62 | 63 | 64 | 65 | 66 | 71 | net:192.168.1.123:8008~shs:FCX/tsDLpubCPKKfIrw4gc+SQkHcaD17s7GI6i/ziWY= 88 | 94 | base64(public key) 105 | 111 | 117 | port 128 | IP address 139 | 140 | -------------------------------------------------------------------------------- /docs/protocol/img/impl.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 29 | 35 | 36 | 44 | 50 | 51 | 59 | 65 | 66 | 74 | 80 | 81 | 89 | 95 | 96 | 104 | 110 | 111 | 119 | 125 | 126 | 129 | 133 | 134 | 135 | 162 | 174 | 175 | 177 | 178 | 180 | image/svg+xml 181 | 183 | 184 | 185 | 186 | 187 | 192 | 198 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /content/protocol/img/impl.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 29 | 35 | 36 | 44 | 50 | 51 | 59 | 65 | 66 | 74 | 80 | 81 | 89 | 95 | 96 | 104 | 110 | 111 | 119 | 125 | 126 | 129 | 133 | 134 | 135 | 162 | 174 | 175 | 177 | 178 | 180 | image/svg+xml 181 | 183 | 184 | 185 | 186 | 187 | 192 | 198 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /docs/protocol/discovery.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | discovery 9 | 23 | 24 | 27 | 28 | 29 | 30 |
31 | 92 |
93 |

Discovery

94 |

After a user has generated their identity they need to find some 95 | peers to connect to. To connect to a peer you need its public key and 96 | its address using any of the protocol it supports. Typically with 97 | TCP/IP, you would need its IP address and port, but the Scuttlebutt 98 | protocol is not restricted to TCP/IP as transport. The Scuttlebutt 99 | protocol currently has three methods for peers to discover each 100 | other.

101 |

Local network

102 |

Peers constantly broadcast UDP packets on their local network 103 | advertising their presence. The body of each packet is a string 104 | containing the peer’s IP address, port and base64-encoded public key 105 | (without @ or .ed25519):

106 |
107 |

108 | Implementations

109 |

JS:

110 | 116 |

Java:

117 | 121 |
122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 |
Source IPSource portDestination IPDestination port
192.168.1.1238008255.255.255.2558008
140 |
141 | net:192.168.1.123:8008:~shs:FCX/tsDLpubCPKKfIrw4gc+SQkHcaD17s7GI6i/ziWY= 143 | 145 |
146 |

This message format can be any valid multiserver 148 | address. In local networks, this is usually a net 149 | address with an IPv4 or IPv6 address.

150 |

Current implementations broadcast one of these packets every second. 151 | When a peer sees another peer’s broadcast packet they can connect to 152 | exchange messages. Some clients show nearby peers in the user interface 153 | so that the user can see other people on the same network as them.

154 |

UDP source and destination ports are set to the same port number that 155 | the peer is listening on TCP for peer connections (normally 8008).

156 |

Invite code

157 |

Invite codes help new users get connected to 158 | their first pub, which is a Scuttlebutt peer that is 159 | publicly accessible over the internet. An invite code contains a pub’s 160 | domain name, port and public key.

161 |

They also contain a secret key that the user can redeem to make the pub follow them back. This lets the new user see 164 | messages posted by other members of the pub and share their own 165 | messages. Invite codes are the most common way for new users to get 166 | started on Scuttlebutt.

167 |

Pub operators can distribute invite codes any way they see fit, for 168 | example by posting them on existing social networks. Some pubs have a 169 | web page that anybody can visit to generate an invite code.

170 |

Pub message

171 |

Users can post a message to their own feed 172 | advertising a pub:

173 |

Here the user @FCX/ts… is advertising that they know of 174 | pub @VJM7w1… along with the pub’s domain name and port.

175 |
{
176 |   "author": "@FCX/tsDLpubCPKKfIrw4gc+SQkHcaD17s7GI6i/ziWY=.ed25519",
177 |   "content": {
178 |     "type": "pub",
179 |     "address": {
180 |       "host": "one.butt.nz",
181 |       "port": 8008,
182 |       "key": "@VJM7w1W19ZsKmG2KnfaoKIM66BRoreEkzaVm/J//wl8=.ed25519"
183 |     }
184 |   },
185 |   …
186 | }
187 |

When others see this message they can make a note that this pub 188 | exists and connect to it in the future.

189 |

Pub messages are a useful way to find additional peers if you already 190 | know a few. Obviously this doesn’t work for new users who don’t know 191 | anyone else yet and therefore can’t see any pub messages.

192 |
193 |
194 | 195 | -------------------------------------------------------------------------------- /docs/protocol/cryptography.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | cryptography 9 | 23 | 24 | 27 | 28 | 29 | 30 |
31 | 92 |
93 |

Cryptography

94 |

Keys and identities

95 |

The first thing a user needs to participate in Scuttlebutt is an 96 | identity. An identity is an Ed25519 key pair and typically represents a 97 | person, a device, a server or a bot. It’s normal for a person to have 98 | several Scuttlebutt identities.

99 |

Upon starting for the first time, Scuttlebutt clients will 100 | automatically generate an Ed25519 key pair and save it in the user’s 101 | home folder under .ssb/secret.

102 |
103 | The Scuttlebutt identity is a long-term Ed25519 key pair. 105 | 107 |
108 |

Because identities are long and random, no coordination or permission 109 | is required to create a new one, which is essential to the network’s 110 | design.

111 |

Later, a user can choose to give themselves a nickname or avatar to 112 | make themselves easier to refer to. Over time nicknames may change but 113 | identities stay the same. If a user loses their secret key or has it 114 | stolen they will need to generate a new identity and tell people to use 115 | their new one instead.

116 |

The public key of an identity is presented to users and transmitted 117 | in some parts of the network protocol using this format:

118 |
119 | @FCX/tsDLpubCPKKfIrw4gc+SQkHcaD17s7GI6i/ziWY=.ed25519 where everything but the @ prefix and .ed25519 suffix is the public-key, base64-encoded. 121 | 125 |
126 |

Throughout the protocol all instances of base64 are the variant that 127 | uses + and /. The final padding = 128 | is also required.

129 |

The beginning @ sign signifies that this represents a 130 | public key rather than a message or blob, which start with 131 | % and &. Each identity has an associated 132 | feed, which is a list of all the messages posted by 133 | that identity. This is why the identity is also called a feed 134 | ID.

135 |

Cryptographic primitives

136 |

The Scuttlebutt protocol relies on NaCl/libsodium’s cryptobox 137 | primitives. This guide uses the following:

138 |

nacl_scalarmult(n, p)

139 |

This is Libsodium’s 141 | scalar multiplication function, which takes two scalars (usually 142 | public and/or secret keys). It has the useful property that, given two 143 | key pairs (pk1, sk1) and (pk2, sk2), 144 | nacl_scalarmult(sk1, pk2) == nacl_scalarmult(sk2, pk1), 145 | which allows shared secret derivation between peers who know each 146 | other’s public key. More on this later.

147 |

nacl_auth(msg, key) and 148 | assert_nacl_auth_verify(authenticator, msg, key)

149 |

This functions are Libsodium’s 151 | message authentication function. The former takes a message and 152 | returns a 32-bytes authenticator, that acts as a detacted signature of 153 | the message. The latter verifies this authenticator is indeed valid for 154 | the given message and key; and errors if they don’t.

155 |

nacl_secret_box(msg, nonce, key) and 156 | assert_nacl_secretbox_open(ciphertext, nonce, key)

157 |

These function are based on Libsodium’s 159 | crypto_secretbox_easy and crypto_secretbox_open_easy function, which 160 | use symmetric cryptography to, respectively, encrypt+authenticate, and 161 | verify+decrypt a message using a nonce and a shared secret.

162 |

nacl_sign_detached(msg, key) and 163 | assert_nacl_sign_verify_detached(sig, msg, key)

164 |

The former is computed from Libsodium’s 166 | signature functions. Unlike the usual Libsodium/NaCl functions, they 167 | work with signatures in independent buffers, rather than concatenated 168 | with the msg.

169 |

pk_to_curve25519(ed25519_pk) and 170 | sk_to_curve25519(ed25519_sk)

171 |

These functions convert Ed25519 keys (used for cryptobox) to 172 | Curve25519 (aka X25519) keys, used for signing. They are implemented 174 | by Libsodium as crypto_sign_ed25519_pk_to_curve25519 and 175 | crypto_sign_ed25519_sk_to_curve25519, respectively.

176 |
177 |
178 | 179 | -------------------------------------------------------------------------------- /docs/protocol/img/room_perspective.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | room 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Alice 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Bob 41 | 42 | 43 | 44 | 45 | Tunnelled Connection 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | A 69 | 70 | 71 | 72 | 73 | B 74 | 75 | 76 | 77 | 78 | 79 | means 80 | A 81 | connects directly to 82 | B 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | A 107 | 108 | 109 | 110 | 111 | B 112 | 113 | 114 | 115 | 116 | 117 | means 118 | A 119 | connects indirectly to 120 | B 121 | 122 | 123 | 124 | 125 | 126 | 127 | --------------------------------------------------------------------------------