diff --git a/public/_redirects b/public/_redirects index 591356bbd..3fa144266 100644 --- a/public/_redirects +++ b/public/_redirects @@ -1 +1,9 @@ /faq https://radicle.dev/faq 301 +/guides/getting-started https://radicle.dev/guides/quick-start/ 301 +/guides/getting-started/ https://radicle.dev/guides/quick-start/ 301 +/guides/protocol https://radicle.dev/guides/protocol/ 301 +/guides/protocol/ https://radicle.dev/guides/protocol/ 301 +/guides/seeder https://radicle.dev/guides/seeder/ 301 +/guides/seeder/ https://radicle.dev/guides/seeder/ 301 +/guides/user https://radicle.dev/guides/user/ 301 +/guides/user/ https://radicle.dev/guides/user/ 301 diff --git a/public/marketing/marketing.css b/public/marketing/marketing.css index fb4f0da49..8ab72afaa 100644 --- a/public/marketing/marketing.css +++ b/public/marketing/marketing.css @@ -205,7 +205,7 @@ } /* On section-based pages, each top-level block rises and fades in, staggered, - when the page mounts. Markdown docs (glossary, guides) are a flat list + when the page mounts. Markdown docs (glossary, download) are a flat list of paragraphs, so they rise in as a single block instead. */ @keyframes section-rise { from { @@ -233,7 +233,7 @@ animation-delay: 350ms; } -/* Markdown docs (glossary, guides) are a flat element list. Animate the +/* Markdown docs (glossary, download) are a flat element list. Animate the elements with the same timing (no stagger) so the doc rises in as one block. This lives on the children, not the reused .markdown-content container, so it replays on client-side navigation and not only on hard reload. */ @@ -349,90 +349,6 @@ line-height: 1.6; } -.marketing .guide-hero { - height: 20rem; - object-fit: cover; - border-radius: var(--border-radius-small); - margin-bottom: 2rem; -} - -.marketing .guides-markdown .markdown-content { - max-width: min(66rem, 100%); -} - -.marketing .guides-markdown .markdown-content > :not(aside) { - max-width: none; - width: min(42rem, calc(100% - clamp(15rem, 26vw, 20rem) - 2rem)); -} - -.marketing .guides-markdown .markdown-content aside { - position: static; - float: right; - clear: right; - margin-left: 0; - margin-right: 0; - margin-bottom: 1rem; - margin-inline-start: 0; - width: clamp(15rem, 26vw, 20rem); - font: var(--txt-medium-14); - line-height: 1.6; - color: var(--color-text-tertiary); - background: none !important; - overflow-wrap: anywhere; - word-break: break-word; -} - -@media (max-width: 72rem) { - .marketing .guides-markdown .markdown-content { - max-width: min(62rem, 100%); - } - - .marketing .guides-markdown .markdown-content > :not(aside) { - width: min(38rem, calc(100% - clamp(13.5rem, 30vw, 17rem) - 1.25rem)); - } - - .marketing .guides-markdown .markdown-content aside { - width: clamp(13.5rem, 30vw, 17rem); - margin-inline-start: 1.25rem; - } -} - -@media (max-width: 56rem) { - .marketing .guides-markdown .markdown-content > :not(aside) { - width: min(36rem, calc(100% - clamp(12rem, 32vw, 15rem) - 1rem)); - } - - .marketing .guides-markdown .markdown-content aside { - width: clamp(12rem, 32vw, 15rem); - margin-inline-start: 1rem; - } -} - -@media (max-width: 44rem) { - .marketing .guides-markdown .markdown-content { - max-width: 48rem; - } - - .marketing .guides-markdown .markdown-content > :not(aside) { - max-width: 100%; - width: 100%; - } - - .marketing .guides-markdown .markdown-content aside { - float: none; - clear: none; - width: auto; - margin: 1rem 0; - padding: 0.75rem 1rem; - background: var(--color-surface-subtle) !important; - overflow-wrap: anywhere; - } - - .marketing .guides-markdown .markdown-content aside p { - margin: 0; - } -} - .marketing .markdown-content h1 { font: var(--txt-bold-48); letter-spacing: -0.02em; diff --git a/src/lib/router.ts b/src/lib/router.ts index f6aace796..a39c1beab 100644 --- a/src/lib/router.ts +++ b/src/lib/router.ts @@ -496,7 +496,7 @@ export function extractBaseUrl(hostAndPort: string): BaseUrl { // identifiers are decoded: revision and path segments are deliberately kept // encoded, because branch and tag lookups are keyed on the encoded name. function urlToRoute(url: URL): Route | null { - // Normalize a trailing slash (`/guides/protocol/` → `/guides/protocol`) so + // Normalize a trailing slash (`/glossary/` → `/glossary`) so // both the strict marketing resolver and the lenient repo resolver agree. // Without this the residual empty segment makes marketing routes 404 while // repo routes silently absorb it. `|| "/"` keeps the root path intact. diff --git a/src/marketing/Cli.svelte b/src/marketing/Cli.svelte index c241c8558..f48fe2c59 100644 --- a/src/marketing/Cli.svelte +++ b/src/marketing/Cli.svelte @@ -3,7 +3,6 @@ import CopyCommand from "@app/marketing/CopyCommand.svelte"; import Meta from "@app/marketing/Meta.svelte"; import PlatformBadges from "@app/marketing/PlatformBadges.svelte"; - import { link } from "@app/marketing/link"; const features = [ { @@ -125,8 +124,12 @@
did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
- did:key
- method.rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
- rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi-
z42hL2jL4XNk6K8oHQaSWfMgCL7ji and
- peer z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi. rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji
- z42hL2jL4XNk6K8oHQaSWfMgCL7ji’s canonical
- references.9767b485c2aad1e23097d2b5165287ba84cfa452 refs/heads/master -f3eaa7454e3a4714885905ae99f616fc7895b5fa refs/cobs/xyz.radicle.patch/fe31d5b6049583a42c21a543545d182b893aa4a0 -0590b78ee42b39087983e4de04164065e5aa11bc refs/cobs/xyz.radicle.patch/ffbb812ad6e7fe1c5c610b1246ca5ca9d7d16027-
refs blob under a user’s refs/rad/sigrefs branch.
- $ rad issue
-╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ ● ID Title Author │
-├──────────────────────────────────────────────────────────────────────────────────────────────────┤
-│ ● 80464b3 Categorize initial dataset paxel (you) │
-│ ● e4255cc Establish data standards for IUI Dark Star Data group paxel (you) │
-│ ● 3e2f653 Add anomalous data from ship 897AF calyx z6Mkgom…unurCap │
-│ ● badda04 Recruit Neboriens to join IUI and share dark star data calyx z6Mkgom…unurCap │
-╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
-
-
-Let’s display an issue that looks interesting, using `rad issue show`:
-
-```
-$ rad issue show 3e2f65
-╭─────────────────────────────────────────────────────────────╮
-│ Title Add anomalous data from ship 897AF │
-│ Issue 3e2f653383f0d2fe21ef4e859a25925c364c740a │
-│ Author calyx │
-│ Status open │
-│ │
-│ Before we onboard new IUI members like the Neborians, it is │
-│ important we upload the data from ship 897AF. I just │
-│ reviewed the database and it seems this is missing. │
-╰─────────────────────────────────────────────────────────────╯
-```
-
-The issue, originally opened by Calyx, the repository’s delegate, outlines a task that aligns perfectly with our knowledge area.
-
-> 👾
->
-> _Full identifiers do not need to be used in most commands!_
->
-> Notice that the full identifier is displayed under the `Issue` field. When working with COBs, it’s always possible to use the first few characters of a COB’s ID instead of the full hash, as we did above with `rad issue show`.
-
-
-
-Let’s compose a comment, with `rad issue comment`, expressing our interest in tackling the issue.
-
-```
-$ rad issue comment 3e2f65 --message "I can help."
-╭─────────────────────────╮
-│ paxel (you) now 74faf0e │
-│ I can help. │
-╰─────────────────────────╯
-```
-
-This time, we used the command-line `--message` option to provide a comment, but if you don’t specify any option, you will be prompted to enter a message via your editor, just like with `rad issue open`.
-
-> 🧠
->
-> Since Radicle’s COB types are extensible by users, globally-unique _Type IDs_, such as `xyz.radicle.issue` and `xyz.radicle.patch` are used to identify them, preventing naming collisions. This reverse domain name notation not only houses Radicle’s predefined COBs but also accommodates user-defined ones.
->
-> For example, if a user or organization were to define a new COB type under their domain, it might look something like `com.acme.task`, for some hypothetical “task” COB under the `acme.com` domain. This extensibility allows for an unlimited set of new collaboration primitives to be defined by users.
->
-> If you’re interested in creating a custom COB, pop into our [chatroom][zulip] and we can get you started.
-
-### Assigning issues
-
-Now, let’s enter Calyx’s point of view for a second. Calyx is the maintainer of the repository and is starting his day, gazing into a fresh terminal window, where he enters `rad inbox`.
-
-$ rad inbox
-╭────────────────────────────────────────────────────────────────────────────────────────────╮
-│ dark-star │
-├────────────────────────────────────────────────────────────────────────────────────────────┤
-│ 004 ● 3e2f653 Add anomalous data from ship 897AF issue open calyx 1 hour ago │
-╰────────────────────────────────────────────────────────────────────────────────────────────╯
-
-
-The Radicle inbox is like a notifications center, displaying issues and patches that are newly created or have unread comments or new activity.
-
-> 👾
->
-> If you run `rad inbox` from a Radicle repository, only notifications belonging to this repository will be shown. You can override this by using `--all` to show notifications from all repositories. If the command is run outside a Radicle repository, all repositories will be displayed.
-
-Calyx decides to display the notification in his inbox with `rad inbox show`, using the notification number.
-
-```
-$ rad inbox show 4
-╭─────────────────────────────────────────────────────────────╮
-│ Title Add anomalous data from ship 897AF │
-│ Issue 3e2f653383f0d2fe21ef4e859a25925c364c740a │
-│ Author calyx │
-│ Status open │
-│ │
-│ Before we onboard new IUI members like the Neborians, it is │
-│ important we upload the data from ship 897AF. I just │
-│ reviewed the database and it seems this is missing. │
-├─────────────────────────────────────────────────────────────┤
-│ paxel z6MkvZw…7aCGq3C 1 hour ago 74faf0e │
-│ I can help. │
-╰─────────────────────────────────────────────────────────────╯
-```
-
-He sees that we (Paxel) left a comment and we’re interested in helping with the issue, and decides to assign it to us with `rad issue assign`.
-
-```
-$ rad issue assign 3e2f653 --add did:key:z6MkvZwzK64f3GuDcAs6dEcje89ddfHkBjS1v9Dkh7aCGq3C
-```
-
-He adds a comment to the issue as well.
-
-```
-$ rad issue comment 3e2f653 --message "Great, I've assigned it to you."
-```
-
-> 👾
->
-> The `assign` sub-command can be used to add (`--add`) or remove (`--delete`) assignees.
-
-As a delegate of `dark-star`, Calyx has the rights to assign this issue. Calyx can then use `rad issue show`, to confirm that the issue was correctly assigned:
-
-```
-$ rad issue show 3e2f65
-╭─────────────────────────────────────────────────────────────╮
-│ Title Add anomalous data from ship 897AF │
-│ Issue 3e2f653383f0d2fe21ef4e859a25925c364c740a │
-│ Author calyx │
-│ Assignees paxel z6MkvZw…7aCGq3C │
-│ Status open │
-│ │
-│ Before we onboard new IUI members like the Neborians, it is │
-│ important we upload the data from ship 897AF. I just │
-│ reviewed the database and it seems this is missing. │
-├─────────────────────────────────────────────────────────────┤
-│ paxel z6MkvZw…7aCGq3C 6 hours ago 39134b6 │
-│ I can help. │
-├─────────────────────────────────────────────────────────────┤
-│ calyx 2 minutes ago 922fac7 │
-│ Great, I've assigned it to you. │
-╰─────────────────────────────────────────────────────────────╯
-```
-
-> 👾
->
-> There’s a lot more that you can do with issues, such as:
->
-> - Editing with `rad issue edit`
-> - Labeling with `rad issue label`
-> - Closing with `rad issue state --closed`
->
-> We don’t want to overwhelm you though! Check out `rad issue --help` for more details.
-
-### Working with _patches_
-
-The collaboration voyage doesn’t stop at issues though! In the realm of code, issues typically lead to _patches_.
-
-Since Calyx has assigned the above issue to us, we’ll start working on it by making the changes in a new branch, and once we’re done, we’ll open a patch. Let’s dive into it step-by-step.
-
-First, we need to make sure we’re in our working copy of the `dark-star` repository. You can always check the current Radicle repository using the `rad .` invocation.
-
-```
-$ rad .
-rad:z3cyotNHuasWowQ2h4yF9c3tFFdvc
-```
-
-Now, let’s create a new branch where we’ll add the missing data from ship 897AF, as mentioned in the issue. We happen to have this data right on our system!
-
-```
-$ git checkout -b anomalous-data-897af
-$ cp /var/run/897af.log data/897af.log
-$ git add data/
-```
-
-Finally, we commit our changes to the branch.
-
-```
-$ git commit -m "Add anomalous data from ship 897AF"
-```
-
-Now that our changes are committed, we’re ready to open a patch. To do so, we simply push our branch to `refs/patches`, on `rad` remote.
-
-
-
-```
-$ git push rad HEAD:refs/patches
-```
-
-Radicle will then open your editor, where the patch title can be edited and a description can be added. Just like for issues, the first line of text is the patch title, while the subsequent lines make up the description.
-
-> 👾
->
-> In the above command, `HEAD:refs/patches` is a _refspec_, where `HEAD` is a reference to the last commit in the currently checked-out branch, and `refs/patches` is a (magic) reference on the remote side. But you don’t have to use `HEAD`; any local branch will do. Even an arbitrary commit hash can be used to open a patch!
-
-
-
-Once we’ve entered the details describing the patch, we save and exit our editor. This pushes our commit to the `rad` remote, opening the patch.
-
--✓ Patch e5f0a5a5adaa33c3b931235967e4930ece9bb617 opened -✓ Synced with 8 node(s) - -To rad://z3cyotNHuasWowQ2h4yF9c3tFFdvc/z6MkvZwzK64f3GuDcAs6dEcje89ddfHkBjS1v9Dkh7aCGq3C -* [new reference] HEAD -> refs/patches -- -In the above Git output, we see our patch’s identifier displayed: - -``` -e5f0a5a5adaa33c3b931235967e4930ece9bb617 -``` - -Patches are a type of COB 🌽, just like issues, and this is the SHA-1 hash that uniquely identifies this patch. - -> 👾 -> -> If you want to avoid remembering the `git push rad HEAD:refs/patches` command, you can add an alias to your local (or global) Git configuration: -> -> ``` -> $ git config alias.patch 'push rad HEAD:refs/patches' -> ``` -> -> This allows you to create a patch by simply running `git patch`. Use the `--global` option with `git config` to add the alias to your global Git config. - -> 🧠 -> -> Radicle’s patch workflow is implemented using the `git-remote-rad` helper program developed for Radicle. This program is called by Git when it detects a push to any `rad://` URL, such as the one under our `rad` remote. - -In addition to opening a patch, the above `push` command creates a new remote tracking branch and makes it the upstream of our `anomalous-data-897af` branch. Any subsequent push from this original branch will now update the patch, synchronizing our local changes with the network. - -> 🧠 -> -> The name of the remote patch branch is always `rad/patches/` followed by the patch identifier. For example: -> -> ``` -> rad/patches/e5f0a5a5adaa33c3b931235967e4930ece9bb617 -> ``` - -We can take a look at remote tracking branches using `git branch --remotes` to understand this better. - -``` -$ git branch --remotes - calyx@z6Mkgom1bTxdh9fMFxFNXFMw3SbXnma6NARdsfcFuunurCap/main - rad/main - rad/patches/e5f0a5a5adaa33c3b931235967e4930ece9bb617 -``` - -Here we see three branches: the `main` branch of Calyx (`calyx@z6Mk..`), our own main branch (`rad/main`), and the branch associated with our recent patch (`rad/patches/e5f0a..`). - -We can also check the commit hashes associated with the `rad` remote references: - -``` -$ git ls-remote rad -e81db74197f48cc5198d9c03cceaec955af82abf refs/heads/main -80b8d420658834afc444a13c03f0c3ff6875a71c refs/heads/patches/e5f0a5a5adaa33c3b931235967e4930ece9bb617 -``` - -Since the `rad` remote is associated with our published copy of the repository, these are the branch heads that are publicly accessible on the network. - -### Updating patches - -So let’s say we’d like to update the patch we just submitted. We found some formatting issues with the log file we included and we’d like to correct it and update the patch. - -First, let’s dive into our editor and fix the formatting: - -``` -$ vi data/897af.log -``` - -Then, we commit the changes and push. - -``` -$ git commit --amend -$ git push --force -``` - -To update the patch, we simply use `git push`. Since we amended the original commit, we have to use the `--force` option when pushing. This is common practice when a patch has been reworked. - -As with opening a patch, this opens an editor to enter a reason for updating the patch. Leave it blank if you’d like to skip that. - -
✓ Patch e5f0a5a updated to revision 4d0c1156f8ac7af2297d1314cd7556185cd16ae4
-✓ Synced with 6 node(s)
-
-To rad://z3cyotNHuasWowQ2h4yF9c3tFFdvc/z6MkvZwzK64f3GuDcAs6dEcje89ddfHkBjS1v9Dkh7aCGq3C
- + b766431...80b8d42 anomalous-data-897af -> patches/e5f0a5a5adaa33c3b931235967e4930ece9bb617 (forced update)
-
-
-We see that the change that was pushed is represented by a new identifier:
-
-```
-4d0c1156f8ac7af2297d1314cd7556185cd16ae4
-```
-
-This identifier represents the current version of the patch. Patch versions, called _revisions_ in Radicle, are immutable. Therefore, each update to a patch creates a new revision, and patch updates are non-destructive, even when you force-push.
-
-
-
-> 👾
->
-> If the branch upstream is not set to the patch reference, eg. `rad/patches/e5f0a5a..`, you can set it with `rad patch set e5f0a5a`.
-
-As with issues, we can display the patch using the `show` sub-command:
-
-$ rad patch show e5f0a5a
-╭────────────────────────────────────────────────────────────────────────────────╮
-│ Title Add anomalous data from ship 897AF │
-│ Patch e5f0a5a5adaa33c3b931235967e4930ece9bb617 │
-│ Author paxel (you) │
-│ Head 80b8d420658834afc444a13c03f0c3ff6875a71c │
-│ Branches anomalous-data-897af │
-│ Commits ahead 1, behind 0 │
-│ Status open │
-│ │
-│ Data from ship 897AF. │
-├────────────────────────────────────────────────────────────────────────────────┤
-│ 80b8d42 Add anomalous data from ship 897AF │
-├────────────────────────────────────────────────────────────────────────────────┤
-│ ● opened by paxel (you) (b766431) 58 minutes ago │
-│ ↑ updated to 4d0c1156f8ac7af2297d1314cd7556185cd16ae4 (80b8d42) 44 minutes ago │
-╰────────────────────────────────────────────────────────────────────────────────╯
-
-
-Notice at the bottom that there is a timeline, with the original patch head (`b766431`) and the updated head (`80b8d42`) after a new revisions (`4d0c115`) was created via a patch update.
-
-Revisions are much more than changesets; they can carry useful metadata such as reviews, comments, and a top-level description that spans all the commits under it. That’s why you see two identifiers for the revision, the first one is the hash of the revision, and the second is the hash of the code itself (eg. your branch head).
-
-> 🧠
->
-> All patches start with an _initial_ revision. This revision has the same identifier as the patch itself, ie. `e5f0a5a5adaa33c3b931235967e4930ece9bb617` in the example above.
-
-### Merging patches into the canonical
-
-Now that we’ve opened a patch, let’s switch perspectives again and return to Calyx-land. Calyx will want to `checkout` the patch, review it, possibly make changes and run tests, and then `merge` it. Let’s walk through this process together.
-
-First, within the repository, Calyx runs `rad patch` and notices there is a new patch.
-
-$ rad patch
-╭───────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ ● ID Title Author Head + - Updated │
-├───────────────────────────────────────────────────────────────────────────────────────────────────┤
-│ ● e5f0a5a Add anomalous data from ship 897AF z6MkvZw…7aCGq3C 80b8d42 +50382 -0 1 hour ago │
-╰───────────────────────────────────────────────────────────────────────────────────────────────────╯
-
-
-Calyx then uses the `checkout` sub-command of `patch` to switch to the patch:
-
-```
-$ rad patch checkout e5f0a5a
-✓ Switched to branch patch/e5f0a5a at revision 4d0c1156f8ac7af2297d1314cd7556185cd16ae4
-✓ Branch patch/e5f0a5a setup to track rad/patches/e5f0a5a5adaa33c3b931235967e4930ece9bb617
-```
-
-When collaborating with peers, it’s often useful to be able to checkout a patch in its own branch. With a patch checkout, you can browse the code, run tests and even propose your own revision to the patch.
-
-The `checkout` sub-command switches to a new local branch which has the patch checked out at a specific revision. By default, the latest of the patch author’s revisions is chosen, but you can specify a different revision using the `--revision` option. See `rad patch --help` for more information.
-
-> 👾
->
-> If you’d just like to view the patch changes without checking out the code, use the `diff` command!
->
-> ```
-> rad patch diff e5f0a5a
-> ```
-
-It’s a rare occasion that someone submits a perfect patch that can be merged as-is. For example, in some cases a maintainer may want to rebase the patch’s commits on top of the main branch, which could have moved since the patch was proposed.
-
-Calyx will do just that by running the following command from the patch checkout:
-
-```
-$ git rebase main
-```
-
-This replays the patch’s commits on top of the current `main` branch. All that’s left to do is to update the patch itself, with a new revision:
-
-```
-$ git push -o patch.message="Rebased!" --force
-✓ Patch e5f0a5a updated to revision 1e0596d917725c447106b1efc900bebf4b95a810
-✓ Synced with 12 node(s)
-
-To rad://z3cyotNHuasWowQ2h4yF9c3tFFdvc/z6Mkgom1bTxdh9fMFxFNXFMw3SbXnma6NARdsfcFuunurCap
-* [new branch] patch/e5f0a5a -> patches/e5f0a5a5adaa33c3b931235967e4930ece9bb617
-```
-
-Although Calyx isn’t the author of the patch he can propose a new revision. In fact, _anyone_ can do that, and you will see their revision if you follow them!
-
-Since Calyx did a rebase, the `--force` flag was required. Notice also the `-o patch.message` option; this allows Calyx to directly provide a comment related to this revision, bypassing his editor.
-
-> 🧠
->
-> Git has something called “push options”, these are options that are passed to the remote helper and/or server when executing a push, and can be specified via `-o $ git remote -v
-paxel rad://z3cyotNHuasWowQ2h4yF9c3tFFdvc/z6MkvZwzK64f3GuDcAs6dEcje89ddfHkBjS1v9Dkh7aCGq3C (fetch)
-paxel rad://z3cyotNHuasWowQ2h4yF9c3tFFdvc/z6MkvZwzK64f3GuDcAs6dEcje89ddfHkBjS1v9Dkh7aCGq3C (push)
-rad rad://z3cyotNHuasWowQ2h4yF9c3tFFdvc (fetch)
-rad rad://z3cyotNHuasWowQ2h4yF9c3tFFdvc/z6Mkgom1bTxdh9fMFxFNXFMw3SbXnma6NARdsfcFuunurCap (push)
-
-
-In the above output, we see the names of the remotes on the left. We see that the `paxel` remote points to a different namespace than the `rad` remote, which points to Calyx’s namespace. Remotes can also be viewed using `rad remote`:
-
-```
-$ rad remote
-paxel z6MkvZwzK64f3GuDcAs6dEcje89ddfHkBjS1v9Dkh7aCGq3C (fetch)
-rad (canonical upstream) (fetch)
-rad z6Mkgom1bTxdh9fMFxFNXFMw3SbXnma6NARdsfcFuunurCap (push)
-```
-
-Now that a remote is setup, Calyx can fetch our branches with `git fetch`:
-
-```
-$ git fetch paxel
- From rad://z3cyotNHuasWowQ2h4yF9c3tFFdvc/z6MkvZwzK64f3GuDcAs6dEcje89ddfHkBjS1v9Dkh7aCGq3C
- * [new branch] dark-star-secrets -> paxel/dark-star-secrets
-```
-
-Oh darn, what has he found! If you don’t want a branch to be published on the Radicle network, avoid pushing it to your `rad` remote!
-
-> 👾
->
-> You can always use `rad remote list --untracked` to find peers that you are following but are not tracking in your working copy.
-
-### Synchronizing the canonical
-
-Now that Calyx has updated the repository’s `main` branch by merging our patch, all we need to do is pull those changes ourselves, to have the most up to date state.
-
-We’ll start by switching back to our `main` branch with `git checkout main`, and then we’ll initiate a regular `pull`:
-
-```
-$ git pull
-Fetching rad
-From rad://z3cyotNHuasWowQ2h4yF9c3tFFdvc
- e81db74..25f8515 main -> rad/main
-Updating e81db74..25f8515
-Fast-forward
-...
-```
-
-> 🧠
->
-> The `git pull` command is really just a `git fetch` followed by a `git merge`. If you’re comfortable with Git, it can be a good idea to first run the `fetch`, see which remote-tracking branches were updated, and decide what to merge yourself.
-
-There we go, our working copy is now up to date with Calyx’s.
-
-If you get `Already up to date.`, it could mean that the changes pushed by Calyx haven’t reached your node yet. In that case, it’s a good idea to ask your node to fetch changes from the network:
-
-```
-$ rad sync --fetch
-✓ Fetching rad:z3cyotNHuasWowQ2h4yF9c3tFFdvc from z6MkrLM…ocNYPm7..
-✓ Fetching rad:z3cyotNHuasWowQ2h4yF9c3tFFdvc from z6Mkg5Z…4Q8qCHw..
-✓ Fetched repository from 2 seed(s)
-```
-
-After the `sync` command succeeds, try to `git pull` again.
-
-And, that’s a wrap! You now know the most relevant nitty gritty details of how to leverage Radicle for code collaboration.
-
-_One thing to keep in mind is that Radicle doesn’t have advanced code review features yet for patches, but that is coming in a near-term release._
-
-## 3. Selectively revealing repositories
-
-In his influential work, [A Cypherpunk’s Manifesto][cyph-man] (1993), Eric Hughes eloquently encapsulated the essence of privacy, stating: **_Privacy is the power to selectively reveal oneself to the world._** This notion of self-determination and control over one’s personal information stands in stark contrast to the reality of using “private repository” features on traditional centralized code forges. In those systems, privacy is merely an illusion maintained by terms of service agreements, as data is stored in cleartext, fully accessible to the platform’s employees, vulnerable to potential misuse or leaks, and subject to external pressures such as government requests. Users are left with no real control, forced to blindly trust that their code and data won’t be viewed, exploited, or even used without consent for purposes like training large language models.
-
-In contrast, Radicle’s decentralized approach puts the power of privacy directly in the hands of users by leveraging public key cryptography and granular access control mechanisms. The _power to selectively reveal_ is a foundational principle in Radicle, fostering a more resilient and safer environment for code collaboration. Through selective replication, private repositories are only accessible to and replicated by repository collaborators, while fully encrypted connections ensure confidential data transfer between peers.
-
-By combining selective replication and encrypted and authenticated connections, repositories are invisible to the rest of the network. This approach effectively creates isolated spaces for private collaboration, forgoing the need for encryption at rest.
-
-In this chapter, we’ll explore how to maintain sovereignty and control over a private repository while collaborating with a set of peers and a trusted seed node.
-
-### Initializing a private repository
-
-When a private repository is created in Radicle, it is only visible and accessible to explicitly authorized peers. Privacy is extended to all aspects of the repository, including its data, creation time, Repository ID (RID), Collaborative Objects (COBs), and its set of collaborators.
-
-To maintain this privacy during data transfer, all connections between nodes, including those to seed nodes, are fully encrypted using an AEAD algorithm, `ChaCha20-Poly1305`.
-
-
-
-To demonstrate how this works in practice, let’s create a private repository for a sensitive research project we’re working on with our peer, Calyx. We’ll start by initializing a repository within a new directory named `schrödingers-paradox` and committing our initial research findings:
-
-```
-$ cd schrödingers-paradox
-$ git init
-$ cp ~/secrets/897af-captain.log data/897af-captain.log
-$ git commit -a -m "Add captain's log from ship 897AF"
-```
-
-Next, to securely share this with Calyx, we initialize it as a private repository in Radicle using the `--private` flag:
-
-
-
-```
-$ rad init --private
-```
-
-This invocation functions similarly to the public repository initialization process; the key difference is that it automatically configures the `visibility` option of the repository to private. As a result, the repository remains unannounced 🥷 and unpublished on the network, reflecting its nature.
-
-```
-$ rad init --private
-
-Initializing private radicle 👾 repository in /home/paxel/src/schrödingers-paradox..
-...
-
-Your Repository ID (RID) is rad:z3jEQE4VMzkR1UVeSLiN9E8AMtV6a.
-You can show it any time by running `rad .` from this directory.
-
-You have created a private repository.
-This repository will only be visible to you, and to peers you explicitly allow.
-
-To make it public, run `rad publish`.
-To push changes, run `git push`.
-```
-
-If we run `rad ls --private`, we’ll find the repository with its `private` visibility.
-
--╭────────────────────────────────────────────────────────────────────────╮ -│ Name RID Visibility │ -├────────────────────────────────────────────────────────────────────────┤ -│ schrödingers-paradox rad:z3jEQE4VMzkR1UVeSLiN9E8AMtV6a private │ -╰────────────────────────────────────────────────────────────────────────╯ -- -### Curating the _allow_ list - -Now that we have our private repository, the next step is to grant access to our collaborators. - -The newly created private repository, `schrödingers-paradox`, will initially be accessible only by us, the lone delegate. This is due to the fact that repository delegates always have access to their private repositories. However, to collaborate on this repository with others, we need to curate its allow list to include the DIDs of Calyx, and at least one [seed node][seeder] _we trust_. This seed node will act as a trusted relay, necessary for the synchronization of repository state between Calyx and us. - -Though this trusted seed node needs a public DNS address, it doesn’t have to be advertised to the network. One can simply omit adding the address to the `externalAddresses` key in the Radicle node configuration. - -> 👻 -> -> Private repositories are not encrypted at rest, so any node that you add to the allow list will have visibility over the contents of your private repository. In the [Chapter 4][ch4] we explore a different setup that works over Tor and doesn’t require a trusted seed node. - -We can add both Calyx and our trusted seed node to the allow list using a single `rad id update` command. This command requires a `--title` for the update, along with each authorized DID specified with the `--allow` flag: - -``` -$ rad id update --title "Allow Calyx and seed.darkstar.example" \ - --allow did:key:z6Mkgom1bTxdh9fMFxFNXFMw3SbXnma6NARdsfcFuunurCap \ - --allow did:key:z6MkqNZS9QWvC4wbZ8Vz4hQZ1FzN8q4XGj2KGmK9qNgQ8VWt -``` - -Once we submit the update above, it is automatically approved, as we are the repository’s sole delegate. If you look at the resulting diff, you see that both Calyx and our trusted node are being added to the `allow` list under the `visibility` key: - -```json -"allow": [ - "did:key:z6Mkgom1bTxdh9fMFxFNXFMw3SbXnma6NARdsfcFuunurCap", - "did:key:z6MkqNZS9QWvC4wbZ8Vz4hQZ1FzN8q4XGj2KGmK9qNgQ8VWt" -] -``` - -> 👾 -> -> It is also possible to update a repository that is currently public to become private, with a repository identity update: -> -> ``` -> $ rad id update --title "Make private" --visibility private -> ``` -> -> This won’t delete historic repository data from public seed nodes or other peers that previously seeded or cloned the repository, meaning users will still be able to clone the old version of the repository. -> -> Instead, future updates to the repository, including the repository’s private status will be hidden to unauthorized users, only to be announced and accessible to those explicitly defined in the `allow` list. - -### Replicating to our seed node - -Now that we’ve set up our private repository, it’s essential to configure it from our trusted seed node. Since our seed node is likely configured with a selective seeding policy, we’ll have to tell it to seed our private repository with the `rad seed` command. - -Since `schrödingers-paradox` is a private repository, we also need to specify our (Paxel’s) Node ID using the `--from` flag to explicitly indicate a source node to fetch from. We therefore run the following command from within an SSH session on our seed node: - - - -``` -$ rad seed rad:z3jEQE4VMzkR1UVeSLiN9E8AMtV6a --from z6MkvZwzK64f3GuDcAs6dEcje89ddfHkBjS1v9Dkh7aCGq3C -``` - -This will update darkstar’s seeding policy and attempt to fetch the repository from our node. For the fetching to be successful, we’ll have to be connected to the seed node. Check that it is indeed the case with `rad node status`, and use `rad node connect` or adjust your node’s configuration if it isn’t the case. - -> 👾 -> -> If you’re experiencing issues with repository replication, try these steps: -> -> 1. Check your current connections: -> ``` -> $ rad node status -> ``` -> 2. If your seed node isn’t listed, manually connect using: -> ``` -> $ rad node connect -> ``` -> For example: -> ``` -> $ rad node connect z6MkqNZS9QWvC4wbZ8Vz4hQZ1FzN8q4XGj2KGmK9qNgQ8VWt@seed.darkstar.example:8776 -> ``` -> -> To find your seed node’s address, run `rad node config --addresses` while logged in to the seed node. -> -> For comprehensive information on setting up and managing seed nodes, refer to our [Seeder’s Guide][seeder]. - -Once the repository is on the seed, you should be able to see it by running `rad ls --private` from within the SSH session. It can then serve as an always online reliable access point for other authorized peers. Remember, only peers on the repository’s allow list will be able to fetch from this seed node. - -### Cloning as a trusted peer - -With our repository now successfully seeded, Calyx can clone our project. Since `schrödingers-paradox` is a private repository, Calyx needs to specify the seed node to fetch from, using the `--seed` flag. In this case, Calyx specifies the Node ID of `seed.darkstar.example`: - - - -``` -$ rad clone rad:z3jEQE4VMzkR1UVeSLiN9E8AMtV6a --seed z6MkqNZS9QWvC4wbZ8Vz4hQZ1FzN8q4XGj2KGmK9qNgQ8VWt -``` - -Once the repository is fetched, it’s no longer necessary to specify the seed. Commands such as `rad sync` will automatically try to sync with our trusted seed. - -_With a local copy of the repository, Calyx can now collaborate freely, in full secrecy._ - -### Removing nodes from the _allow_ list - -In the event that our seed node became compromised, we could revoke access privileges to the repository from it. The same `rad id update` command is used, but this time with the `--disallow` flag: - -``` -$ rad id update --title "Darkstar is compromised!" \ - --disallow did:key:z6MkqNZS9QWvC4wbZ8Vz4hQZ1FzN8q4XGj2KGmK9qNgQ8VWt -``` - -Our _no-longer-trusted_ seed node will now be unable to fetch updates from us for `schrödingers-paradox`. - -> 👾 -> -> As time goes by, you might decide that a repository no longer needs to be private. In such a case, you can easily change its visibility. Simply run `rad publish` within a repository’s working copy to transform it from private to public. - -> 👻 -> -> Radicle’s codebase hasn’t yet undergone formal security audits. While we’re confident in its security, undiscovered vulnerabilities may exist. If you encounter any issues, we encourage you to report them to **security@radicle.xyz**. - -## 4. Embracing the Onion - -**[Tor][tor]**, “The Onion Router”, is a decentralized network that anonymizes users’ online activity by routing internet traffic through multiple encrypted relays, providing stronger anonymity compared to traditional VPNs. - - - -To further enhance repository and user privacy, Radicle nodes can be configured to run behind Tor. This allows connections to be made to `.onion` addresses, as well as hiding one’s IP address and exposing Radicle as a Tor _onion service_. - -Depending on how you configure Radicle, it offers several key advantages: - -- **Enhanced anonymity**: Your node’s real IP address is hidden, making it difficult for others to track your online activities or determine your location. -- **NAT traversal**: Tor automatically handles [NAT traversal][nat], eliminating the need for manual port forwarding or UPnP configuration. This means peers can directly connect with one another without a seed node, even behind a restrictive firewall. -- **Censorship resistance**: Repositories accessible via Tor are more resilient against censorship attempts, as the Tor network is designed to circumvent blocking. - -[nat]: https://en.wikipedia.org/wiki/NAT_traversal - -Network Address Translation (NAT) typically creates barriers for direct peer-to-peer communication. Devices behind NAT gateways are often isolated with non-routable private IP addresses, making them inaccessible from the public internet. This is common in most consumer internet setups. - -Tor addresses this issue by providing static `.onion` addresses – cryptographic identifiers representing nodes that are routable across the Tor network. This allows peers behind NAT to connect directly without needing to know each other’s public IP addresses, overcoming limitations faced by traditional peer-to-peer networks. - -This chapter will explore a few distinct ways to configure Radicle with Tor: - -1. **Mixed Mode**: Only connections to `.onion` addresses go through Tor, while other connections work normally. -2. **Full Proxy Mode**: All traffic is routed through Tor for maximum anonymity, - -``` -including traffic to nodes that are *not* behind Tor. -``` - -3. **Transparent Proxy Mode**: Leverages an existing fully transparent Tor proxy on the network. - -Before proceeding, ensure you have Tor installed on your system. Use your preferred package manager to install it (the package name is typically “tor”). For detailed instructions, refer to the Tor [installation guide][install-tor]. - -### Setting up a Tor Onion Service for Radicle - -We’ve been collaborating with Calyx on the private `schrödingers-paradox` repository. Recently, we removed a compromised seed node from the repository’s `allow` list, which inadvertently cut off Calyx’s access to the repository. - -To restore Calyx’s access and enhance the security of our sensitive research, we’ll configure our node as a Tor Onion Service (also known as a hidden service). This setup will make our node accessible via a `.onion` address, allowing Calyx to connect directly to us without relying on any intermediaries. - -Let’s set up our Tor Onion Service. The following steps are for Linux-based systems, but they should be similar for other operating systems. - -First we create a new `radicle` directory for our Onion Service, ensuring it’s readable and writable: - -``` -$ sudo mkdir -m 700 /var/lib/tor/radicle -``` - -This directory will store information and cryptographic keys for our Onion Service. - -Then we update our Tor configuration (`/etc/tor/torrc`) by adding the following lines to it: - -``` -HiddenServiceDir /var/lib/tor/radicle -HiddenServicePort 8776 -``` - -These lines set the `HiddenServiceDir` to the path of the directory we just created and specify the `HiddenServicePort` as `8776`, which is the default Radicle node port. - -Finally, we restart our Tor daemon to apply the configuration changes: - -``` -$ sudo systemctl restart tor -``` - -Our `.onion` address is stored in a file under our Onion Service directory. Let’s take a look: - -``` -$ sudo cat /var/lib/tor/radicle/hostname -1odmmeotgcfx65l5hn6ejkaruvai222vs7o7tmtllszqk5xbysolfdd.onion -``` - -We can copy this address to our clipboard, as we’ll need it to configure our Radicle node in the next steps. - -> 🧠 -> -> If you are a **macOS** user, install Tor via `brew install tor` and restart it via `brew services restart tor`. -> -> Use `mkdir -p -m 700 ~/.tor/radicle` to create your onion service directory. -> -> The Tor configuration file is located at `/opt/homebrew/etc/tor/torrc` and your `.onion` address can be found in `~/.tor/radicle/hostname`. -> -> Make sure to set the torrc HiddenServiceDir to `/Users/