Compare commits

...

30 Commits

Author SHA1 Message Date
Mark Qvist 8661a3886b Prepare release 2026-05-05 20:01:08 +02:00
Mark Qvist 2ddbef70fe Improved markdown, micron and syntax highlight rendering consistency and accuracy 2026-05-05 19:54:39 +02:00
Mark Qvist bb051e5a11 Added markdown handling to markdown-to-micron converter 2026-05-05 19:09:31 +02:00
Mark Qvist 080085e813 Cleanup 2026-05-05 18:25:48 +02:00
Mark Qvist 85454b1f25 Updated version 2026-05-05 18:14:32 +02:00
Mark Qvist 3f5653f650 Added admin permission type in rngit 2026-05-05 18:12:42 +02:00
Mark Qvist b1357eb146 Updated documentation 2026-05-05 17:43:51 +02:00
Mark Qvist 7731e799f4 Implemented rngit work doc management 2026-05-05 17:40:57 +02:00
Mark Qvist 15320e4d2c Added interact permission to rngit 2026-05-05 12:41:09 +02:00
Mark Qvist 78596b687a Cleanup 2026-05-05 11:08:05 +02:00
Mark Qvist 729dc8dc11 Updated readme 2026-05-05 02:33:06 +02:00
Mark Qvist 3c08eb8122 Updated readme 2026-05-05 02:32:13 +02:00
Mark Qvist 9d12c86ac8 Updated readme 2026-05-05 02:29:52 +02:00
Mark Qvist 3bd573688c Updated readme 2026-05-05 02:29:09 +02:00
Mark Qvist 07ff87974e Prepare release 2026-05-05 01:19:43 +02:00
Mark Qvist e8fa92950d Fixed missing unquote 2026-05-05 01:18:07 +02:00
Mark Qvist ab6532742e Prepare release 2026-05-05 01:00:51 +02:00
Mark Qvist 4e583770e5 Updated docs 2026-05-05 00:57:26 +02:00
Mark Qvist f9b6dc2ab8 Added transfer progress to release artifact uploads for rngit 2026-05-04 23:55:03 +02:00
Mark Qvist 1c2bc0c7b8 Added file downloads to rngit 2026-05-04 22:49:56 +02:00
Mark Qvist 05760f914c Added latest release meta-tag support 2026-05-04 21:17:31 +02:00
Mark Qvist 3f6e8605af Cleanup 2026-05-04 20:58:49 +02:00
Mark Qvist b6bfd1655c Updated version 2026-05-04 20:53:31 +02:00
Mark Qvist 8cbd0e22ff Added artifact file serving to rngit 2026-05-04 20:48:20 +02:00
Mark Qvist 15ec64e974 Added rngit release management 2026-05-04 20:14:39 +02:00
Mark Qvist 3de16e085e Added releases to rngit page node 2026-05-04 20:13:35 +02:00
Mark Qvist 4cbd4ed60c Added basic release management scaffold 2026-05-04 15:28:28 +02:00
Mark Qvist b8fbd616e5 Added release permission to rngit 2026-05-04 14:10:23 +02:00
Mark Qvist f8a79d2f51 Catch tunnel synthesis errors and log 2026-05-04 12:56:31 +02:00
Mark Qvist 0218ff4e26 Cleanup 2026-05-04 02:08:31 +02:00
42 changed files with 4611 additions and 903 deletions
+47
View File
@@ -1,3 +1,50 @@
### 2026-05-05: RNS 1.2.3
This release adds Work Document and update/commenting support to `rngit`.
**Changes**
- Added Work Document management to `rngit`.
- Added Work pages to the page node of `rngit`.
- Added `interact` permission type to `rngit`.
- Added `admin` permission type to `rngit`.
- Added markdown blockquote support to the `rngit` markdown-to-micron converter.
- Improved markdown-to-micron conversion and syntax highlighting accuracy in `rngit`.
**Release Hashes**
```
8562130f297a6b33be9d72c449bbe6ae83cad41e1530e0fa112f5fa545a3f364 rns-1.2.3-py3-none-any.whl
0862f46a08e610add1bcac0916c6554f3e79590ab2765900178d5e1f1f0c7026 rnspure-1.2.3-py3-none-any.whl
```
**Release Signatures**
Release artifacts include `rsg` signature files that can be validated against the RNS release signing identity `<bc7291552be7a58f361522990465165c>` using `rnid`:
```sh
rnid -i bc7291552be7a58f361522990465165c -V rns-1.2.2-py3-none-any.whl.rsg
```
### 2026-05-05: RNS 1.2.2
This release adds release management workflows to the `rngit` utility. Downloading files and release artifacts from `rngit` will require the latest version of Nomad Network. Other nomadnet clients *may* have to update their file download link handling, if they don't already support passing query parameters for file download links.
**Changes**
- Added release management to `rngit`.
- Added release pages to the page node of `rngit`.
- Added file downloads in the tree browser of `rngit`.
**Release Hashes**
```
4bf0a376a9778de8a91b9ec8a5bc4b929be928eede8784b20022c7fe52bbce62 rns-1.2.2-py3-none-any.whl
d85f8b765dcf718d284388b249ca0e48e785f250bb41773a83e159e46c5bcf70 rnspure-1.2.2-py3-none-any.whl
```
**Release Signatures**
Release artifacts include `rsg` signature files that can be validated against the RNS release signing identity `<bc7291552be7a58f361522990465165c>` using `rnid`:
```sh
rnid -i bc7291552be7a58f361522990465165c -V rns-1.2.2-py3-none-any.whl.rsg
```
### 2026-05-04: RNS 1.2.1
This release adds a nomadnet Git page node to the `rngit` utility.
+2 -2
View File
@@ -1,6 +1,6 @@
>> Reticulum Network Stack
To understand the foundational philosophy and goals of this system, read the [Zen of Reticulum](Zen%20of%20Reticulum.md).
To understand the foundational philosophy and goals of this system, read the `!`[Zen of Reticulum`:/page/blob.mu`g=reticulum|r=reticulum|ref=HEAD|path=Zen+of+Reticulum.md]`!.
Reticulum is the cryptography-based networking stack for building local and wide-area networks with readily available hardware. It can operate even with very high latency and extremely low bandwidth. Reticulum allows you to build wide-area networks with off-the-shelf tools, and offers end-to-end encryption and connectivity, initiator anonymity, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable delivery acknowledgements and more.
@@ -16,7 +16,7 @@ No kernel modules or drivers are required. Reticulum runs completely in userland
The full documentation for Reticulum is available at [markqvist.github.io/Reticulum/manual/](https://markqvist.github.io/Reticulum/manual/).
You can also download the [Reticulum manual as a PDF](https://github.com/markqvist/Reticulum/raw/master/docs/Reticulum%20Manual.pdf) or [as an e-book in EPUB format](https://github.com/markqvist/Reticulum/raw/master/docs/Reticulum%20Manual.epub).
You can also download the `!`[Reticulum manual as a PDF`:/file/download`g=reticulum|r=reticulum|ref=HEAD|path=docs%2FReticulum+Manual.pdf]`! or `!`[as an e-book in EPUB format`:/file/download`g=reticulum|r=reticulum|ref=HEAD|path=docs%2FReticulum+Manual.pdf]`!.
For more info, see [reticulum.network](https://reticulum.network/) and [the FAQ section of the wiki](https://github.com/markqvist/Reticulum/wiki/Frequently-Asked-Questions).
-3
View File
@@ -691,9 +691,6 @@ class Resource:
RNS.log(f"Decompressed resource exceeded maximum decompressed size. The resource was rejected.", RNS.LOG_ERROR)
return
if self.compressed: self.data = bz2.decompress(data)
else: self.data = data
calculated_hash = RNS.Identity.full_hash(self.data+self.random_hash)
if calculated_hash == self.hash:
if self.has_metadata and self.segment_index == 1:
+17 -14
View File
@@ -2222,24 +2222,27 @@ class Transport:
@staticmethod
def synthesize_tunnel(interface):
interface_hash = interface.get_hash()
public_key = RNS.Transport.identity.get_public_key()
random_hash = RNS.Identity.get_random_hash()
tunnel_id_data = public_key+interface_hash
tunnel_id = RNS.Identity.full_hash(tunnel_id_data)
try:
interface_hash = interface.get_hash()
public_key = RNS.Transport.identity.get_public_key()
random_hash = RNS.Identity.get_random_hash()
tunnel_id_data = public_key+interface_hash
tunnel_id = RNS.Identity.full_hash(tunnel_id_data)
signed_data = tunnel_id_data+random_hash
signature = Transport.identity.sign(signed_data)
data = signed_data+signature
signed_data = tunnel_id_data+random_hash
signature = Transport.identity.sign(signed_data)
data = signed_data+signature
tnl_snth_dst = RNS.Destination(None, RNS.Destination.OUT, RNS.Destination.PLAIN, Transport.APP_NAME, "tunnel", "synthesize")
tnl_snth_dst = RNS.Destination(None, RNS.Destination.OUT, RNS.Destination.PLAIN, Transport.APP_NAME, "tunnel", "synthesize")
packet = RNS.Packet(tnl_snth_dst, data, packet_type = RNS.Packet.DATA, transport_type = RNS.Transport.BROADCAST, header_type = RNS.Packet.HEADER_1, attached_interface = interface)
packet.send()
packet = RNS.Packet(tnl_snth_dst, data, packet_type = RNS.Packet.DATA, transport_type = RNS.Transport.BROADCAST, header_type = RNS.Packet.HEADER_1, attached_interface = interface)
packet.send()
interface.wants_tunnel = False
interface.wants_tunnel = False
except Exception as e: RNS.log(f"Could not synthesize tunnel for {interface}: {e}", RNS.LOG_ERROR)
@staticmethod
def tunnel_synthesize_handler(data, packet):
+1 -2
View File
@@ -122,7 +122,6 @@ class ReticulumGitClient():
self.ref_batch_size = self.REF_BATCH_SIZE
self.remote_refs = {}
# Response progress tracking
self.response_progress = 0
self.previous_progress = 0
self.response_size = None
@@ -252,7 +251,7 @@ class ReticulumGitClient():
percent = round(self.response_progress * 100, 1)
size = self.response_size
rxd = size*self.response_progress
speed_kbps = (self.response_speed / 1024) if hasattr(self, 'response_speed') else 0
speed_kbps = (self.response_speed / 1000) if hasattr(self, 'response_speed') else 0
sys.stderr.write(f"Transferring: {percent}% ({RNS.prettysize(rxd)}/{RNS.prettysize(size)}) {RNS.prettyspeed(self.response_speed)} \r")
sys.stderr.flush()
+12 -2
View File
@@ -237,13 +237,23 @@ class MicronFormatter:
if color and value:
escaped = self._escape_value(value)
output_parts.append(f"`FT{color}{escaped}`f")
if escaped.startswith("\n"): ilb = "\n"; escaped = escaped[1:]
else: ilb = ""
if escaped.endswith("\n"): tlb = "\n"; escaped = escaped[:-1]
else: tlb = ""
output_parts.append(f"{ilb}`FT{color}{escaped}`f{tlb}")
else: output_parts.append(self._escape_value(value))
prev_was_dot = is_dot
outfile.write("".join(output_parts))
output = "".join(output_parts)
final_output = ""
for line in output.splitlines():
if line.startswith(">"): line = f"`>{line}"
final_output += f"{line}\n"
outfile.write(final_output)
def _get_color_key_for_token(self, ttype):
token_parts = []
+592 -47
View File
@@ -56,6 +56,12 @@ class NomadNetworkNode():
PATH_COMMIT = "/page/commit.mu"
PATH_REFS = "/page/refs.mu"
PATH_STATS = "/page/stats.mu"
PATH_RELEASES = "/page/releases.mu"
PATH_RELEASE = "/page/release.mu"
PATH_WORK = "/page/work.mu"
PATH_WORK_DOC = "/page/work_doc.mu"
PATH_ARTIFACT = "/file/artifact"
PATH_DOWNLOAD = "/file/download"
BLOB_SIZE_LIMIT = 256 * 1024
TREE_ENTRIES_PER_PAGE = 1000
@@ -73,6 +79,8 @@ class NomadNetworkNode():
U_ICON_COMMITS = "🖹"
U_ICON_STATS = "🗠"
U_ICON_HEART = ""
U_ICON_PACKAGE = ""
U_ICON_WORK = ""
NF_ICON_SEP = ""
NF_ICON_FOLDER = "󰉖"
@@ -82,6 +90,8 @@ class NomadNetworkNode():
NF_ICON_COMMITS = "󰋚"
NF_ICON_STATS = ""
NF_ICON_HEART = "󰋑"
NF_ICON_PACKAGE = "󰏗"
NF_ICON_WORK = "󱌣"
CLR_FOLDER = "`Ffe6"
CLR_FILE = "`F66d"
@@ -94,32 +104,36 @@ class NomadNetworkNode():
def __init__(self, owner=None):
if not owner: raise TypeError(f"Invalid owner {owner} for {self}")
self._ready = False
self._should_run = False
self.owner = owner
self.identity = owner.identity
self.node_name = owner.node_name
self.announce_interval = owner.announce_interval
self.last_announce = 0
self.null_ident = RNS.Identity.from_bytes(bytes(64))
self._ready = False
self._should_run = False
self.owner = owner
self.identity = owner.identity
self.node_name = owner.node_name
self.announce_interval = owner.announce_interval
self.last_announce = 0
self.null_ident = RNS.Identity.from_bytes(bytes(64))
self.templates = {}
self.templates["base"] = DEFAULT_BASE_TEMPLATE
self.templates["front"] = DEFAULT_FRONT_TEMPLATE
self.templates["group"] = DEFAULT_GROUP_TEMPLATE
self.templates["repo"] = DEFAULT_REPO_TEMPLATE
self.templates["tree"] = DEFAULT_TREE_TEMPLATE
self.templates["blob"] = DEFAULT_BLOB_TEMPLATE
self.templates["commits"] = DEFAULT_COMMITS_TEMPLATE
self.templates["commit"] = DEFAULT_COMMIT_TEMPLATE
self.templates["refs"] = DEFAULT_REFS_TEMPLATE
self.templates["stats"] = DEFAULT_STATS_TEMPLATE
self.templatesdir = self.owner.configdir+"/templates"
self.use_nerdfonts = self.USE_NERDFONTS
self.highlight_syntax = True
self.highlighter = SyntaxHighlighter()
self.mdc = MarkdownToMicron(max_width=self.MAX_RENDER_WIDTH, syntax_highlighter=self.highlighter)
self.thanks_deque = deque(maxlen=256)
self.templates = {}
self.templates["base"] = DEFAULT_BASE_TEMPLATE
self.templates["front"] = DEFAULT_FRONT_TEMPLATE
self.templates["group"] = DEFAULT_GROUP_TEMPLATE
self.templates["repo"] = DEFAULT_REPO_TEMPLATE
self.templates["releases"] = DEFAULT_RELEASES_TEMPLATE
self.templates["release"] = DEFAULT_RELEASE_TEMPLATE
self.templates["tree"] = DEFAULT_TREE_TEMPLATE
self.templates["blob"] = DEFAULT_BLOB_TEMPLATE
self.templates["commits"] = DEFAULT_COMMITS_TEMPLATE
self.templates["commit"] = DEFAULT_COMMIT_TEMPLATE
self.templates["refs"] = DEFAULT_REFS_TEMPLATE
self.templates["stats"] = DEFAULT_STATS_TEMPLATE
self.templates["work"] = DEFAULT_WORK_TEMPLATE
self.templates["work_doc"] = DEFAULT_WORK_DOC_TEMPLATE
self.templatesdir = self.owner.configdir+"/templates"
self.use_nerdfonts = self.USE_NERDFONTS
self.highlight_syntax = True
self.highlighter = SyntaxHighlighter()
self.mdc = MarkdownToMicron(max_width=self.MAX_RENDER_WIDTH, syntax_highlighter=self.highlighter)
self.thanks_deque = deque(maxlen=256)
if not os.path.isdir(self.templatesdir):
try: os.makedirs(self.templatesdir)
@@ -151,6 +165,8 @@ class NomadNetworkNode():
elif name == "tag": return self.NF_ICON_TAG
elif name == "stats": return self.NF_ICON_STATS
elif name == "heart": return self.NF_ICON_HEART
elif name == "package": return self.NF_ICON_PACKAGE
elif name == "work": return self.NF_ICON_WORK
else: return ""
else:
@@ -162,6 +178,8 @@ class NomadNetworkNode():
elif name == "tag": return self.U_ICON_TAG
elif name == "stats": return self.U_ICON_STATS
elif name == "heart": return self.U_ICON_HEART
elif name == "package": return self.U_ICON_PACKAGE
elif name == "work": return self.U_ICON_WORK
else: return ""
def jobs(self):
@@ -186,15 +204,21 @@ class NomadNetworkNode():
return self.owner.resolve_permission(remote_identity, group_name, repository_name, permission)
def register_request_handlers(self):
self.destination.register_request_handler(self.PATH_INDEX, response_generator=self.serve_front_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_GROUP, response_generator=self.serve_group_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_REPO, response_generator=self.serve_repo_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_TREE, response_generator=self.serve_tree_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_BLOB, response_generator=self.serve_blob_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_COMMITS, response_generator=self.serve_commits_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_COMMIT, response_generator=self.serve_commit_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_REFS, response_generator=self.serve_refs_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_STATS, response_generator=self.serve_stats_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_INDEX, response_generator=self.serve_front_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_GROUP, response_generator=self.serve_group_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_REPO, response_generator=self.serve_repo_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_TREE, response_generator=self.serve_tree_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_BLOB, response_generator=self.serve_blob_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_COMMITS, response_generator=self.serve_commits_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_COMMIT, response_generator=self.serve_commit_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_REFS, response_generator=self.serve_refs_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_STATS, response_generator=self.serve_stats_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_RELEASES, response_generator=self.serve_releases_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_RELEASE, response_generator=self.serve_release_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_WORK, response_generator=self.serve_work_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_WORK_DOC, response_generator=self.serve_work_doc_page, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_ARTIFACT, response_generator=self.serve_artifact, allow=RNS.Destination.ALLOW_ALL)
self.destination.register_request_handler(self.PATH_DOWNLOAD, response_generator=self.serve_download, allow=RNS.Destination.ALLOW_ALL)
def get_template(self, template):
filename = f"{template}.mu"
@@ -317,7 +341,7 @@ class NomadNetworkNode():
group_name = data.get("var_g", "") if data else ""
if not group_name:
content = self.m_heading("Error", 1) + "\nInvalid request.\n"
content = self.m_heading("Error", 2) + "\nInvalid request\n"
return self.render_template(content, st=st)
accessible_repos = self.get_accessible_repositories(remote_identity, group_name)
@@ -363,7 +387,7 @@ class NomadNetworkNode():
thanks = True if data.get("var_thanks", "") else False
if not group_name or not repo_name:
content = self.m_heading("Error", 1) + "\nInvalid request.\n"
content = self.m_heading("Error", 2) + "\nInvalid request\n"
return self.render_template(content, st=st)
content_parts = []
@@ -394,9 +418,22 @@ class NomadNetworkNode():
commits_count = self.get_commit_count(repo["path"], resolved_ref) if resolved_ref else 0
branch_count = len(refs.get("heads", [])) if refs else 0
tag_count = len(refs["tags"]) if refs else 0
active_work_dir = repo["path"]+".work/active"
if not os.path.isdir(active_work_dir): work_count = 0
else:
work_count = len([f for f in os.listdir(active_work_dir) if f.isdigit() and os.path.isdir(os.path.join(active_work_dir, f))])
# Get releases count
releases_path = f"{repo['path']}.releases"
releases_count = 0
releases = self.owner.releases_list_data(releases_path)
if releases: releases_count = len([r for r in releases if r.get("status") == "published"])
sep = self.icon("sep")
content_parts.append(f"{self.m_link_r(self.icon("folder")+" Files", self.PATH_TREE, g=group_name, r=repo_name, ref='HEAD')} {sep} ")
if releases_count: content_parts.append(f"{self.m_link_r(self.icon("package")+f" Releases ({releases_count})", self.PATH_RELEASES, g=group_name, r=repo_name)} {sep} ")
content_parts.append(f"{self.m_link_r(self.icon("work")+f" Work ({work_count})", self.PATH_WORK, g=group_name, r=repo_name)} {sep} ")
content_parts.append(f"{self.m_link_r(self.icon("commits")+f" Commits ({commits_count})", self.PATH_COMMITS, g=group_name, r=repo_name, ref='HEAD')} {sep} ")
content_parts.append(f"{self.m_link_r(self.icon("branch")+f" Branches ({branch_count})", self.PATH_REFS, g=group_name, r=repo_name, type="heads")} {sep} ")
content_parts.append(f"{self.m_link_r(self.icon("tag")+f" Tags ({tag_count})", self.PATH_REFS, g=group_name, r=repo_name, type="tags")} {sep} ")
@@ -421,8 +458,9 @@ class NomadNetworkNode():
content_parts.append(self.m_divider())
else:
content_parts.append(self.m_italic("No README file found in this repository."))
content_parts.append(self.m_divider())
content_parts.append("\n")
content_parts.append(self.m_italic("No README file found in this repository."))
content_parts.append("\n")
@@ -631,13 +669,15 @@ class NomadNetworkNode():
breadcrumb = " / ".join(breadcrumb_parts)
nav_parts.append(">>\n" + breadcrumb + "\n")
if renderable:
dl_link = self.m_link("Download", self.PATH_DOWNLOAD, g=group_name, r=repo_name, ref=ref, path=file_path)
if not renderable: nav_parts.append(f"\n{dl_link}\n")
else:
sep = self.icon("sep")
rnd_link = self.m_link("View rendered", self.PATH_BLOB, g=group_name, r=repo_name, ref=ref, path=file_path, render="y")
raw_link = self.m_link("View raw", self.PATH_BLOB, g=group_name, r=repo_name, ref=ref, path=file_path, raw="y")
if render: render_controls = f"Displaying Rendered {sep} {raw_link}"
else: render_controls = f"Displaying Raw {sep} {rnd_link}"
nav_parts.append(f"\n{render_controls}\n")
nav_parts.append(f"\n{render_controls} {sep} {dl_link}\n")
# Get blob info
blob_info = self.get_blob_info(repo_path, resolved_ref, file_path)
@@ -669,14 +709,14 @@ class NomadNetworkNode():
content = self.get_blob_content(repo_path, resolved_ref, file_path)
if content is not None:
if renderable and render:
if file_ext == ".mu": content_parts.append(f"{content}\n")
elif file_ext == ".md": content_parts.append(f"{self.mdc.format_block(content)}\n")
if file_ext == ".mu": content_parts.append(f"{content.rstrip()}\n")
elif file_ext == ".md": content_parts.append(f"{self.mdc.format_block(content).rstrip()}\n")
else : content_parts.append(f"`=\n{content}\n`=")
else:
if self.highlight_syntax:
highlighted = self.highlighter.highlight(content, file_path)
content_parts.append(highlighted)
highlighted = self.highlighter.highlight(content, file_path).rstrip()
content_parts.append(highlighted+"\n")
else: content_parts.append(f"`=\n{content}\n`=")
@@ -780,7 +820,7 @@ class NomadNetworkNode():
commit_hash = data.get("var_h", "") if data else ""
if not group_name or not repo_name:
content = self.m_heading("Error", 2) + "\nInvalid request.\n"
content = self.m_heading("Error", 2) + "\nInvalid request\n"
return self.render_template(content, st=st)
repo = self.get_accessible_repository(remote_identity, group_name, repo_name)
@@ -825,7 +865,7 @@ class NomadNetworkNode():
commit_info = self.get_commit_info(repo_path, resolved_hash)
if not commit_info:
content_parts.append(self.m_heading("Error", 1) + "\n\nCould not retrieve commit information.\n")
content_parts.append(self.m_heading("Error", 2) + "\n\nCould not retrieve commit information.\n")
page_content = "".join(content_parts)
return self.render_template(page_content, st=st)
@@ -922,7 +962,7 @@ class NomadNetworkNode():
nav_content = "".join(nav_parts)
if not group_name or not repo_name:
content = self.m_heading("Error", 2) + "\nInvalid request.\n"
content = self.m_heading("Error", 2) + "\nInvalid request\n"
return self.render_template(content, st=st)
repo = self.get_accessible_repository(remote_identity, group_name, repo_name)
@@ -1016,7 +1056,7 @@ class NomadNetworkNode():
repo_name = data.get("var_r", "") if data else ""
if not group_name or not repo_name:
content = self.m_heading("Error", 1) + "\nInvalid request.\n"
content = self.m_heading("Error", 2) + "\nInvalid request\n"
return self.render_template(content, st=st)
content_parts = []
@@ -1091,6 +1131,462 @@ class NomadNetworkNode():
nav_content = "".join(nav_parts)
return self.render_template(page_content, nav_content=nav_content, template="stats", st=st)
def serve_releases_page(self, path, data, request_id, link_id, remote_identity, requested_at):
st = time.time()
RNS.log(f"Releases page request from {remote_identity}", RNS.LOG_DEBUG)
if not data: data = {}
group_name = data.get("var_g", "") if data else ""
repo_name = data.get("var_r", "") if data else ""
if not group_name or not repo_name:
content = self.m_heading("Error", 2) + "\nInvalid request\n"
return self.render_template(content, st=st)
repo = self.get_accessible_repository(remote_identity, group_name, repo_name)
if not repo:
content = self.m_heading("Error", 2) + "\nThe requested repository was not found.\n"
return self.render_template(content, st=st)
content_parts = []
nav_parts = []
# Breadcrumb navigation
breadcrumb = f">>\n{self.m_link("Node", self.PATH_INDEX)} / {self.m_link(group_name, self.PATH_GROUP, g=group_name)} / {self.m_link(repo_name, self.PATH_REPO, g=group_name, r=repo_name)} / releases"
nav_parts.append(breadcrumb + "\n")
nav_content = "".join(nav_parts)
releases_path = f"{repo['path']}.releases"
releases = self.owner.releases_list_data(releases_path)
if not releases:
content_parts.append(self.m_heading("Releases", 2))
content_parts.append("\nNo releases available for this repository.\n")
page_content = "".join(content_parts)
return self.render_template(page_content, nav_content=nav_content, template="repo", st=st)
published_releases = [r for r in releases if r.get("status") == "published"]
content_parts.append(self.m_heading(f"Releases ({len(published_releases)})", 2))
content_parts.append("\n")
for rel in published_releases:
tag = rel.get("tag", "unknown")
created_ts = rel.get("created", 0)
date_str = time.strftime("%Y-%m-%d", time.localtime(created_ts)) if created_ts else "unknown"
artifacts = rel.get("artifacts", 0)
preview = rel.get("preview", "")[:256]
if len(rel.get("preview", "")) > len(preview): preview += ""
link = self.m_link(tag, self.PATH_RELEASE, g=group_name, r=repo_name, t=tag)
sep = self.icon("sep")
artifacts_str = f"`*{artifacts} artifact{'s' if artifacts != 1 else ''}`*"
content_parts.append(f"{link} {self.CLR_DIM}{date_str} {sep} {artifacts_str}`f\n")
if preview: content_parts.append(f"{self.m_escape(preview)}\n")
content_parts.append("\n")
self.owner.view_succeeded(group_name, repo_name, remote_identity)
page_content = "".join(content_parts)
return self.render_template(page_content, nav_content=nav_content, template="releases", st=st)
def serve_release_page(self, path, data, request_id, link_id, remote_identity, requested_at):
st = time.time()
RNS.log(f"Release page request from {remote_identity}", RNS.LOG_DEBUG)
if not data: data = {}
group_name = data.get("var_g", "") if data else ""
repo_name = data.get("var_r", "") if data else ""
tag = data.get("var_t", "") if data else ""
if not group_name or not repo_name or not tag:
content = self.m_heading("Error", 2) + "\nInvalid request\n"
return self.render_template(content, st=st)
repo = self.get_accessible_repository(remote_identity, group_name, repo_name)
if not repo:
content = self.m_heading("Error", 2) + "\nThe requested repository was not found.\n"
return self.render_template(content, st=st)
releases_path = f"{repo['path']}.releases"
if tag == "latest":
releases = self.owner.releases_list_data(releases_path)
if not releases:
content = self.m_heading("Release Not Found", 2) + f"\nNo latest release exist.\n"
return self.render_template(content, nav_content=nav_content, st=st)
recent_releases = sorted(releases, key=lambda x: x['created'], reverse=True)
tag = recent_releases[0]["tag"]
content_parts = []
nav_parts = []
# Breadcrumb navigation
breadcrumb = f">>\n{self.m_link("Node", self.PATH_INDEX)} / {self.m_link(group_name, self.PATH_GROUP, g=group_name)} / {self.m_link(repo_name, self.PATH_REPO, g=group_name, r=repo_name)} / {self.m_link('releases', self.PATH_RELEASES, g=group_name, r=repo_name)} / {tag}"
nav_parts.append(breadcrumb + "\n")
nav_content = "".join(nav_parts)
release_dir = os.path.join(releases_path, tag)
if not os.path.isdir(release_dir):
content = self.m_heading("Release Not Found", 2) + f"\nThe release {tag} does not exist.\n"
return self.render_template(content, nav_content=nav_content, st=st)
release_info = self.owner.release_data(release_dir, tag)
if not release_info:
content = self.m_heading("Error", 2) + "\nCould not load release data.\n"
return self.render_template(content, nav_content=nav_content, st=st)
# Only show published releases
if release_info.get("status") != "published":
content = self.m_heading("Release Not Found", 2) + f"\nThe release {tag} does not exist.\n"
return self.render_template(content, nav_content=nav_content, st=st)
sep = self.icon("sep")
heart = self.icon("heart")
created_ts = release_info.get("created", 0)
ts_str = f" {sep} {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(created_ts))}" if created_ts else ""
content_parts.append(self.m_heading(f"Release {tag}{ts_str}", 2))
content_parts.append("\n")
# Release notes
notes = release_info.get("notes", "")
if notes:
notes_format = release_info.get("notes_format", "text")
if notes_format == "micron": content_parts.append(f"{notes}\n")
elif notes_format == "markdown": content_parts.append(f"{self.mdc.format_block(notes)}\n")
else: content_parts.append(f"`={notes}`=\n")
content_parts.append("\n")
# Artifacts
artifacts = release_info.get("artifacts", [])
if artifacts:
content_parts.append(self.m_heading(f"Artifacts ({len(artifacts)})", 2))
content_parts.append("\n")
for art in artifacts:
name = art.get("name", "unknown")
size = art.get("size", 0)
size_str = RNS.prettysize(size) if size else "0 B"
# content_parts.append(f"{self.icon('file')} {self.m_escape(name)} {self.CLR_DIM}({size_str})`f\n")
lstr_1 = f"{self.icon('file')} {self.m_escape(name)}"
lstr_2 = f"({size_str})"
link_1 = self.m_link_r(lstr_1, self.PATH_ARTIFACT, g=group_name, r=repo_name, t=tag, a=name)
link_2 = self.m_link_r(lstr_2, self.PATH_ARTIFACT, g=group_name, r=repo_name, t=tag, a=name)
content_parts.append(f"{link_1} {self.CLR_DIM}{link_2}`f\n")
content_parts.append("\n")
else:
content_parts.append(self.m_heading("Artifacts", 2))
content_parts.append("\nNo artifacts for this release.\n\n")
thanks = True if data.get("var_thanks", "") else False
thanks_count = self.release_thanks(release_dir, add=thanks, link_id=link_id)
content_parts.append(f"{self.m_link_r(self.icon("heart")+f" Thanks ({thanks_count})", self.PATH_RELEASE, g=group_name, r=repo_name, t=tag, thanks="y")}\n")
self.owner.view_succeeded(group_name, repo_name, remote_identity)
page_content = "".join(content_parts)
return self.render_template(page_content, nav_content=nav_content, template="release", st=st)
def serve_work_page(self, path, data, request_id, link_id, remote_identity, requested_at):
st = time.time()
RNS.log(f"Work page request from {remote_identity}", RNS.LOG_DEBUG)
if not data: data = {}
group_name = data.get("var_g", "") if data else ""
repo_name = data.get("var_r", "") if data else ""
scope = data.get("var_scope", "active") if data else "active"
if scope not in ["active", "completed", "all"]: scope = "active"
if not group_name or not repo_name:
content = self.m_heading("Error", 2) + "\nInvalid request\n"
return self.render_template(content, st=st)
repo = self.get_accessible_repository(remote_identity, group_name, repo_name)
if not repo:
content = self.m_heading("Error", 2) + "\nThe requested repository was not found.\n"
return self.render_template(content, st=st)
content_parts = []
nav_parts = []
# Breadcrumb navigation
breadcrumb = f">>\n{self.m_link("Node", self.PATH_INDEX)} / {self.m_link(group_name, self.PATH_GROUP, g=group_name)} / {self.m_link(repo_name, self.PATH_REPO, g=group_name, r=repo_name)} / work"
nav_parts.append(breadcrumb + "\n")
nav_content = "".join(nav_parts)
# Scope filter links
sep = self.icon("sep")
active_s = "`_" if scope == "active" else ""
cmplt_s = "`_" if scope == "completed" else ""
all_s = "`_" if scope == "all" else ""
filter_links = []
filter_links.append(active_s+self.m_link("Active", self.PATH_WORK, g=group_name, r=repo_name, scope="active")+active_s)
filter_links.append(cmplt_s+self.m_link("Completed", self.PATH_WORK, g=group_name, r=repo_name, scope="completed")+cmplt_s)
filter_links.append(all_s+self.m_link("All", self.PATH_WORK, g=group_name, r=repo_name, scope="all")+all_s)
content_parts.append(f" {sep} ".join(filter_links) + "\n\n")
# Load work documents
work_path = f"{repo['path']}.work"
scopes_to_show = ["active", "completed"] if scope == "all" else [scope]
for s in scopes_to_show:
folder_path = os.path.join(work_path, s)
docs = []
if os.path.isdir(folder_path):
for entry in os.listdir(folder_path):
doc_dir = os.path.join(folder_path, entry)
if not os.path.isdir(doc_dir): continue
try:
doc_id = int(entry)
root_path = os.path.join(doc_dir, "root")
if not os.path.isfile(root_path): continue
doc = self.owner._work_load_document(root_path)
if not doc: continue
meta = doc.get("meta", {})
comment_count = len([f for f in os.listdir(doc_dir) if f.isdigit() and os.path.isfile(os.path.join(doc_dir, f))])
docs.append({ "id": doc_id, "title": meta.get("title", "Untitled"),
"created": meta.get("created", 0), "author": meta.get("author", b""),
"comments": comment_count })
except: continue
docs.sort(key=lambda x: x["created"], reverse=True)
if not docs:
content_parts.append(self.m_heading(f"{s.capitalize()} ({len(docs)})", 2)+f"\n`*No {s} work documents`*\n")
content_parts.append("\n")
else:
content_parts.append(self.m_heading(f"{s.capitalize()} ({len(docs)})", 2))
content_parts.append("\n")
for doc in docs:
doc_title = str(doc.get('title', 'Untitled')[:92])
if len(doc_title) < len(doc.get('title', 'Untitled')): doc_title += ""
title_link = self.m_link(self.icon("file")+" "+doc_title, self.PATH_WORK_DOC, g=group_name, r=repo_name, id=doc["id"], scope=s)
author_str = RNS.prettyhexrep(doc["author"]) if doc["author"] else "unknown"
date_str = time.strftime("%Y-%m-%d", time.localtime(doc["created"])) if doc["created"] else ""
content_parts.append(f"{title_link} {self.CLR_DIM}#{doc['id']}`f\n")
content_parts.append(f"{self.CLR_DIM}{date_str} by {author_str}`f\n")
content_parts.append(f"{self.CLR_DIM}{doc['comments']} updates`f\n") if doc['comments'] else None
content_parts.append("\n")
if content_parts[-1] == "\n": content_parts[-1] = ""
self.owner.view_succeeded(group_name, repo_name, remote_identity)
page_content = "".join(content_parts)
return self.render_template(page_content, nav_content=nav_content, template="work", st=st)
def serve_work_doc_page(self, path, data, request_id, link_id, remote_identity, requested_at):
st = time.time()
RNS.log(f"Work document page request from {remote_identity}", RNS.LOG_DEBUG)
if not data: data = {}
group_name = data.get("var_g", "") if data else ""
repo_name = data.get("var_r", "") if data else ""
doc_id = data.get("var_id", "") if data else ""
scope = data.get("var_scope", "active") if data else "active"
if scope not in ["active", "completed", "all"]: scope = "active"
if not group_name or not repo_name or not doc_id:
content = self.m_heading("Error", 2) + "\nInvalid request\n"
return self.render_template(content, st=st)
try: doc_id = int(doc_id)
except:
content = self.m_heading("Error", 2) + "\nInvalid document ID\n"
return self.render_template(content, st=st)
repo = self.get_accessible_repository(remote_identity, group_name, repo_name)
if not repo:
content = self.m_heading("Error", 2) + "\nThe requested repository was not found\n"
return self.render_template(content, st=st)
work_path = f"{repo['path']}.work"
doc_dir = os.path.join(work_path, scope, str(doc_id))
root_path = os.path.join(doc_dir, "root")
if not os.path.isfile(root_path):
content = self.m_heading("Not Found", 2) + "\nThe requested work document was not found\n"
return self.render_template(content, st=st)
doc = self.owner._work_load_document(root_path)
if not doc:
content = self.m_heading("Error", 2) + "\nCould not load work document\n"
return self.render_template(content, st=st)
content_parts = []
nav_parts = []
# Breadcrumb navigation
breadcrumb = f">>\n{self.m_link("Node", self.PATH_INDEX)} / {self.m_link(group_name, self.PATH_GROUP, g=group_name)} / {self.m_link(repo_name, self.PATH_REPO, g=group_name, r=repo_name)} / {self.m_link('work', self.PATH_WORK, g=group_name, r=repo_name)} / #{doc_id}"
nav_parts.append(breadcrumb + "\n")
nav_content = "".join(nav_parts)
doc_title = doc['meta'].get('title', 'Untitled')[:64]
if len(doc_title) < len(doc['meta'].get('title', 'Untitled')): doc_title += ""
meta = doc.get("meta", {})
author = meta.get("author", b"")
author_str = RNS.prettyhexrep(author) if author else "Unknown"
created = meta.get("created", 0)
edited = meta.get("edited", 0)
fmt = meta.get("format", "markdown")
# Document header
content_parts.append(self.m_heading(f"{doc_title}", 2))
content_parts.append(f"\n{self.CLR_DIM}Author : {author_str}`f\n")
content_parts.append(f"{self.CLR_DIM}Created : {time.strftime('%Y-%m-%d %H:%M', time.localtime(created)) if created else 'unknown'}`f\n")
if edited and edited != created:
content_parts.append(f"{self.CLR_DIM}Edited : {time.strftime('%Y-%m-%d %H:%M', time.localtime(edited))}`f\n")
content_parts.append(f"{self.CLR_DIM}Status : {scope.capitalize()}`f\n\n")
# Document content
content = doc.get("content", "").strip()
if content:
if fmt == "micron": content_parts.append(content)
else: content_parts.append(self.mdc.format_block(content))
content_parts.append("\n")
# Load and display comments
comments = []
if os.path.isdir(doc_dir):
for entry in os.listdir(doc_dir):
if not entry.isdigit(): continue
comment_path = os.path.join(doc_dir, entry)
if not os.path.isfile(comment_path): continue
try:
comment_id = int(entry)
comment = self.owner._work_load_document(comment_path)
if not comment: continue
cmeta = comment.get("meta", {})
comments.append({
"id": comment_id,
"format": comment.get("format", "markdown"),
"content": comment.get("content", ""),
"created": cmeta.get("created", 0),
"author": cmeta.get("author", b"")
})
except: continue
comments.sort(key=lambda x: x["id"])
if comments:
content_parts.append("\n"+self.m_heading(f"Updates ({len(comments)})", 2))
for c in comments:
fmt = c["format"]
if fmt == "markdown": content = self.mdc.format_block(c["content"])
else: content = c["content"]
cauthor_str = RNS.prettyhexrep(c["author"]) if c["author"] else "Unknown"
cdate = time.strftime("%Y-%m-%d %H:%M", time.localtime(c["created"])) if c["created"] else "unknown"
content_parts.append(f"\n{self.CLR_DIM}#{c['id']} by {cauthor_str} on {cdate}`f\n")
content_parts.append(f"{content}\n")
self.owner.view_succeeded(group_name, repo_name, remote_identity)
page_content = "".join(content_parts)
return self.render_template(page_content, nav_content=nav_content, template="work_doc", st=st)
def serve_artifact(self, path, data, request_id, link_id, remote_identity, requested_at):
st = time.time()
RNS.log(f"Artifact file request from {remote_identity}", RNS.LOG_DEBUG)
if not data: data = {}
group_name = data.get("var_g", "") if data else ""
repo_name = data.get("var_r", "") if data else ""
tag = data.get("var_t", "") if data else ""
artifact = data.get("var_a", "") if data else ""
artifact = urllib.parse.unquote_plus(artifact)
if "/" in artifact: return None
if not group_name or not repo_name or not tag or not artifact: None
repo = self.get_accessible_repository(remote_identity, group_name, repo_name)
if not repo:
RNS.log(f"Repository not found or no access for artifact request {group_name}/{repo_name}/{tag}/{artifact}", RNS.LOG_WARNING)
return None
releases_path = f"{repo['path']}.releases"
if tag == "latest":
releases = self.owner.releases_list_data(releases_path)
if not releases: return None
recent_releases = sorted(releases, key=lambda x: x['created'], reverse=True)
tag = recent_releases[0]["tag"]
release_dir = os.path.join(releases_path, tag)
artifacts_dir = os.path.join(release_dir, "artifacts")
artifact_path = os.path.join(artifacts_dir, artifact)
release_info = self.owner.release_data(release_dir, tag)
if not release_info:
RNS.log(f"Could not resolve release info for artifact request {group_name}/{repo_name}/{tag}/{artifact}", RNS.LOG_WARNING)
return None
if release_info.get("status") != "published":
RNS.log(f"Attempt to fetch unpublished artifact {group_name}/{repo_name}/{tag}/{artifact}", RNS.LOG_WARNING)
return None
if not os.path.isdir(release_dir):
RNS.log(f"Release directory not found for artifact request {group_name}/{repo_name}/{tag}/{artifact}", RNS.LOG_WARNING)
return None
if not os.path.isdir(artifacts_dir):
RNS.log(f"Artifacts directory not found for artifact request {group_name}/{repo_name}/{tag}/{artifact}", RNS.LOG_WARNING)
return None
if not os.path.isfile(artifact_path):
RNS.log(f"Artifacts file not found for artifact request {group_name}/{repo_name}/{tag}/{artifact}", RNS.LOG_WARNING)
return None
RNS.log(f"Artifact file resolved for artifact request {group_name}/{repo_name}/{tag}/{artifact}", RNS.LOG_DEBUG)
return [open(artifact_path, "rb"), {"name": artifact.encode("utf-8")}]
def serve_download(self, path, data, request_id, link_id, remote_identity, requested_at):
st = time.time()
RNS.log(f"File download request from {remote_identity}", RNS.LOG_DEBUG)
group_name = data.get("var_g", "") if data else ""
repo_name = data.get("var_r", "") if data else ""
ref = data.get("var_ref", "HEAD") if data else "HEAD"
file_path = data.get("var_path", "") if data else ""
file_path = urllib.parse.unquote_plus(file_path)
file_name = os.path.basename(file_path)
repo = self.get_accessible_repository(remote_identity, group_name, repo_name)
if not repo:
RNS.log(f"Repository not found or no access for download request {group_name}/{repo_name}/{ref}/{file_path}", RNS.LOG_WARNING)
return None
repo_path = repo["path"]
resolved_ref = self.resolve_ref(repo_path, ref)
if not resolved_ref:
RNS.log(f"Ref not found for download request {group_name}/{repo_name}/{ref}/{file_path}", RNS.LOG_WARNING)
return None
if not file_path:
RNS.log(f"No file path for download request {group_name}/{repo_name}/{ref}/{file_path}", RNS.LOG_WARNING)
return None
blob_info = self.get_blob_info(repo_path, resolved_ref, file_path)
if blob_info is None:
RNS.log(f"File not found at ref for download request {group_name}/{repo_name}/{ref}/{file_path}", RNS.LOG_WARNING)
return None
else:
stream = self.get_blob_stream(repo_path, resolved_ref, file_path)
if stream is not None: return [stream, {"name": file_name.encode("utf-8")}]
else:
RNS.log(f"Could not resolve blob stream for download request {group_name}/{repo_name}/{ref}/{file_path}", RNS.LOG_WARNING)
return None
return None
#######################
# Git Data Extraction #
#######################
@@ -1318,6 +1814,19 @@ class NomadNetworkNode():
return None
def get_blob_stream(self, repo_path, ref, path):
file_path = path.strip("/")
try:
proc = subprocess.Popen(["git", "show", f"{ref}:{file_path}"],
cwd=repo_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
return proc.stdout
except subprocess.TimeoutExpired: RNS.log(f"Timeout getting blob content handle", RNS.LOG_WARNING)
except Exception as e: RNS.log(f"Error getting blob content handle: {e}", RNS.LOG_WARNING)
return None
def get_refs_info(self, repo_path, default_branch=None):
refs = {"heads": [], "tags": []}
@@ -1615,6 +2124,31 @@ class NomadNetworkNode():
except Exception as e: RNS.log(f"Error while processing repository thanks for {group_name}/{repo_name}: {e}", RNS.LOG_ERROR)
return 0
def release_thanks(self, release_path, add=False, link_id=None):
if add:
thanks_hash = RNS.Identity.full_hash(link_id+release_path.encode("utf-8"))
if thanks_hash in self.thanks_deque: add = False
else: self.thanks_deque.append(thanks_hash)
try:
thanks_path = f"{release_path}/THANKS"
if not os.path.isfile(thanks_path):
thanks_count = 1 if add else 0
with open(thanks_path, "wb") as fh: fh.write(mp.packb({"count": thanks_count}))
else:
with open(thanks_path, "rb") as fh:
thanks_data = mp.unpackb(fh.read())
if "count" in thanks_data: thanks_count = thanks_data["count"]
else: raise ValueError("Invalid data in thanks file")
if add: thanks_count += 1
with open(thanks_path, "wb") as fh: fh.write(mp.packb({"count": thanks_count}))
return thanks_count
except Exception as e: RNS.log(f"Error while processing release thanks for {group_name}/{repo_name}: {e}", RNS.LOG_ERROR)
return 0
###################
# Stats Renderers #
###################
@@ -1788,6 +2322,12 @@ DEFAULT_GROUP_TEMPLATE = """{PAGE_CONTENT}"""
# Repository page template
DEFAULT_REPO_TEMPLATE = """{PAGE_CONTENT}"""
# Repository page template
DEFAULT_RELEASES_TEMPLATE = """{PAGE_CONTENT}"""
# Repository page template
DEFAULT_RELEASE_TEMPLATE = """{PAGE_CONTENT}"""
# Tree page template
DEFAULT_TREE_TEMPLATE = """{PAGE_CONTENT}"""
@@ -1806,5 +2346,10 @@ DEFAULT_REFS_TEMPLATE = """{PAGE_CONTENT}"""
# Stats page template
DEFAULT_STATS_TEMPLATE = """{PAGE_CONTENT}"""
# Work page templates
DEFAULT_WORK_TEMPLATE = """{PAGE_CONTENT}"""
DEFAULT_WORK_DOC_TEMPLATE = """{PAGE_CONTENT}"""
# Fallback template
FALLBACK_TEMPLATE = """{PAGE_CONTENT}"""
File diff suppressed because it is too large Load Diff
+128 -17
View File
@@ -59,6 +59,9 @@ class MarkdownToMicron:
TABLE_ROW_RE = re.compile(r'^\s*\|?(.+?)\|?\s*$')
TABLE_SEP_RE = re.compile(r'^\s*\|?(?:\s*:?-+:?\s*\|)+\s*$')
# Quote pattern
QUOTE_RE = re.compile(r'^>\s?(.*)$')
# Inline patterns (processed in order of specificity)
LINK_RE = re.compile(r'\[([^\]]+)\]\(([^)]+)\)')
INLINE_CODE_RE = re.compile(r'`([^`]+)`')
@@ -106,6 +109,25 @@ class MarkdownToMicron:
code_buffer = []
in_table = False
table_buffer = []
in_quote = False
quote_buffer = []
def flush_quote_buffer():
nonlocal result_lines, quote_buffer, in_quote
if not quote_buffer:
in_quote = False
return
para = " ".join(quote_buffer)
formatted = self._format_inline(para)
effective_width = self.max_width - 3
if effective_width < 1: effective_width = 1
wrapped_lines = self._wrap_text(formatted, effective_width)
for wrapped_line in wrapped_lines: result_lines.append(f"{wrapped_line}")
quote_buffer = []
in_quote = False
def flush_table_buffer():
nonlocal result_lines, table_buffer, in_table
@@ -157,7 +179,8 @@ class MarkdownToMicron:
is_fence, lang_hint = self._detect_code_fence(line)
if is_fence:
# Flush any pending table before code fence
# Flush any pending structures before code fence
flush_quote_buffer()
flush_table_buffer()
if not in_code_block:
@@ -173,28 +196,51 @@ class MarkdownToMicron:
code_block_lang = None
else:
if in_code_block:
# Buffer code lines for later highlighting
code_buffer.append(line)
# Buffer code lines for later highlighting
if in_code_block: code_buffer.append(line)
else:
if self._is_table_row(line):
if not in_table:
in_table = True
table_buffer = [line]
quote_match = self.QUOTE_RE.match(line)
if quote_match:
if not in_quote:
flush_table_buffer()
in_quote = True
quote_buffer = []
else: table_buffer.append(line)
quote_buffer.append(quote_match.group(1))
else:
# Line breaks table, flush buffer
if in_table: flush_table_buffer()
formatted = self.format_line(line)
result_lines.append(formatted)
if in_quote:
flush_quote_buffer()
if line.strip() != "":
if self._is_table_row(line):
in_table = True
table_buffer = [line]
else:
formatted = self.format_line(line)
result_lines.append(formatted)
# Pass through blank line as separator
else: result_lines.append("")
else:
if self._is_table_row(line):
if not in_table:
in_table = True
table_buffer = [line]
else: table_buffer.append(line)
else:
# Line breaks table, flush buffer
if in_table: flush_table_buffer()
formatted = self.format_line(line)
result_lines.append(formatted)
# Handle unclosed structures
if in_quote: flush_quote_buffer()
if in_table: flush_table_buffer()
if in_code_block:
# Unclosed code block, flush what we have
flush_code_block()
if in_code_block: flush_code_block()
return '\n'.join(result_lines)
@@ -269,7 +315,7 @@ class MarkdownToMicron:
content = match.group(2)
level = len(hashes)
prefix = ">" * min(level, 6)
return f"{prefix}{content}"
return f"{prefix}{self._format_inline(content)}"
def _format_list_item(self, match):
indent = match.group(1)
@@ -581,6 +627,71 @@ class MarkdownToMicron:
truncated = stripped[:width - 1] + ""
return truncated
def _wrap_text(self, text, width):
if not text: return [""]
words = text.split(' ')
lines = []
current_line = ""
current_width = 0
for word in words:
if not word: continue
word_width = self._visible_width(word)
# Check if word alone exceeds width to force break it
if word_width > width:
if current_line:
lines.append(current_line)
current_line = ""
current_width = 0
# Force break the long word character by character
remaining = word
while remaining:
# Binary search for how many characters fit
low, high = 1, len(remaining)
fit_chars = 0
while low <= high:
mid = (low + high) // 2
test_substr = remaining[:mid]
test_width = self._visible_width(test_substr)
if test_width <= width:
fit_chars = mid
low = mid + 1
else:
high = mid - 1
if fit_chars == 0: fit_chars = 1 # Need to force progress
lines.append(remaining[:fit_chars])
remaining = remaining[fit_chars:]
continue
# Check if word fits on current line
space_width = 1 if current_line else 0
if current_width + space_width + word_width <= width:
if current_line:
current_line += " " + word
current_width += space_width + word_width
else:
current_line = word
current_width = word_width
else:
# Flush current line and start new one
lines.append(current_line)
current_line = word
current_width = word_width
# Don't forget the last line
if current_line: lines.append(current_line)
return lines if lines else [""]
def convert_markdown_to_micron(text):
+1 -1
View File
@@ -1 +1 @@
__version__ = "1.2.1"
__version__ = "1.2.3"
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file records the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 30a85b1d5fe4a94e60d7f412c97b4772
config: ea8ffef16f1cae34d273c7bce7123529
tags: 645f666f9bcd5a90fca523b33c5a78b7
+573
View File
@@ -0,0 +1,573 @@
.. _git-main:
******************
Git Over Reticulum
******************
A set of utilities for distributed collaborative software development and publishing is included in RNS.
The system consists of two parts: The ``rngit`` node that hosts repositories, and the ``git-remote-rns`` helper that enables Git to communicate with rngit nodes. As soon as you have RNS installed on your system, you can transparently use Git with Reticulum-hosted repositories just like any other type of remote. Git over Reticulum uses URLs in the following format: ``rns://DESTINATION_HASH/group/repo``.
If you set a branch to track a Reticulum remote as the default upstream, you can simply use ``git`` as you normally would; all commands work transparently and as expected.
.. warning::
**The rngit program is a new addition to RNS!** This functionality was introduced in RNS 1.2.0. While great care has been taken to design a secure, but highly configurable and flexible permission system for allowing many users to interact with many different repositories on a single node, ``rngit`` has not been tested extensively in the wild! Be careful when hosting repositories, especially if they are public or semi-public.
The rngit Utility
=================
The ``rngit`` utility provides full Git repository hosting and interaction over Reticulum. It allows you to host and manage Git repositories and releases on Reticulum nodes, and to interact with remote repositories using standard Git commands through the ``rns://`` URL scheme.
**Usage Examples**
Run ``rngit`` to start a repository node:
.. code:: text
$ rngit
[Notice] Starting Reticulum Git Node...
[Notice] Reticulum Git Node listening on <0d7334d411d00120cbad24edf355fdd2>
On the first run, ``rngit`` will create a default configuration file. You will then need to edit this, to point to your repository locations, configure access permissions, and perform any other necessary configuration.
View your identity and destination hashes:
.. code:: text
$ rngit --print-identity
Git Peer Identity : <959e10e5efc1bd9d97a4083babe51dea>
Repository Node Identity : <153cb870b4665b8c1c348896292b0bad>
Repositories Destination : <0d7334d411d00120cbad24edf355fdd2>
If the page node is enabled, the output will also include the Nomad Network destination hash.
You can run ``rngit`` in service mode with logging to file:
.. code:: text
$ rngit -s
Clone a repository from a remote ``rngit`` node:
.. code:: text
$ git clone rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo
Add a Reticulum remote to an existing repository:
.. code:: text
$ git remote add some_remote rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo
Push changes to the Reticulum remote:
.. code:: text
$ git push some_remote master
Get changes from a remote repository:
.. code:: text
$ git pull rns_remote master
**All Command-Line Options (rngit)**
.. code:: text
usage: rngit.py [-h] [--config CONFIG] [--rnsconfig RNSCONFIG] [-s] [-i] [-v]
[-q] [--version]
Reticulum Git Repository Node
options:
-h, --help show this help message and exit
--config CONFIG path to alternative config directory
--rnsconfig RNSCONFIG
path to alternative Reticulum config directory
-p, --print-identity print identity and destination info and exit
-s, --service rngit is running as a service and should log to file
-i, --interactive drop into interactive shell after initialisation
-v, --verbose increase verbosity
-q, --quiet decrease verbosity
--version show program's version number and exit
**All Command-Line Options (git-remote-rns)**
The ``git-remote-rns`` helper is automatically invoked by Git when interacting with ``rns://`` URLs. It is not typically run directly by users, but accepts the following environment variables for configuration:
- ``RNGIT_CONFIG`` - Path to alternative client configuration directory
- ``RNS_CONFIG`` - Path to alternative Reticulum configuration directory
The client configuration file is located at ``~/.rngit/client_config`` and allows adjusting parameters such as the reference batch size for transfers.
Repository Structure
====================
The ``rngit`` node organizes repositories into groups. Each group is a directory containing bare Git repositories. The repository path format is ``group_name/repo_name``. For example, a repository at ``/var/git/public/myrepo`` would be accessible as ``public/myrepo`` via the URL ``rns://DESTINATION_HASH/public/myrepo``.
**Configuration**
The ``rngit`` node configuration file is located at ``~/.rngit/config`` (or ``/etc/rngit/config`` for system-wide installations). The default configuration includes:
- Repository group paths defining where to find bare repositories
- Access permissions for groups and individual repositories
- Announce intervals for network visibility
- Optional statistics recording for repository activity
Access permissions can be configured at the group level in the config file, or per-repository using ``.allowed`` files. Permissions use the format ``permission:target`` where permission is ``r`` (read), ``w`` (write), ``rw`` (read/write), ``c`` (create) or ``s`` (stats) and target is ``all``, ``none``, or a specific identity hash.
The ``s`` (stats) permission allows viewing repository activity statistics, including views, fetches and pushes over time. To enable statistics recording, set ``record_stats = yes`` in the ``[rngit]`` section of the configuration file. You can also exclude specific identities from statistics by adding their hashes to ``stats_ignore_identities``.
Repository-specific ``.allowed`` files can be static text files or executable scripts that output permission rules to stdout. A ``group.allowed`` file in a repository group directory applies to all repositories within that group.
Serving Pages Over Nomad Network
================================
In addition to providing Git repository access via the Git remote helper protocol, ``rngit`` can also run a `Nomad Network <https://github.com/markqvist/nomadnet>`_ compatible page node. This allows users to browse repository information, view file contents, inspect commit history and access repository statistics through any Nomad Network client.
When enabled, the page node provides a complete interface to your repositories, with automatic Markdown to Micron conversion, syntax-highlighted code browsing, and detailed commit, diff and statistics views.
**Enabling the Git Page Node**
To enable the page node, add the following to your ``~/.rngit/config`` file:
.. code:: text
[pages]
serve_nomadnet = yes
When the page node is enabled, ``rngit`` will listen on a Nomad Network node destination in addition to the Git repository destination. You can view the destination hash by running:
.. code:: text
$ rngit --print-identity
Git Peer Identity : <959e10e5efc1bd9d97a4083babe51dea>
Repository Node Identity : <153cb870b4665b8c1c348896292b0bad>
Repositories Destination : <0d7334d411d00120cbad24edf355fdd2>
Nomad Network Destination : <50824b711717f97c2fb1166ceddd5ea9>
**Accessing Repository Pages**
Once the page node is running, you can access it from any Nomad Network client by connecting to the Nomad Network destination. The page node provides the following views:
- **Front Page** - Lists all repository groups accessible to your identity
- **Group Page** - Shows all repositories within a group
- **Repository Page** - Displays repository overview, description and README
- **Releases** - List of releases for the repository, with information and downloads
- **File Browser** - Browse directory trees and view and download file contents
- **Commits View** - View commit history with pagination
- **Commit Details** - Detailed commit information with file changes and diffs
- **Refs View** - List branches and tags
- **Statistics** - Activity charts showing views, fetches and pushes over time
All pages respect the same permission system used for Git access. If an identity does not have read access to a repository, they will not be able to view its pages.
Formatting & Syntax Highlighting
================================
If the ``pygments`` Python module is installed on your system, the page node will automatically apply syntax highlighting to code files. The highlighting supports a wide range of programming languages and uses a color theme optimized for terminal display.
To enable syntax highlighting, install pygments:
.. code:: text
pip install pygments
**Markdown & Micron Support**
README files and other Markdown documents are automatically converted to Micron markup for display in Nomad Network clients. You can also write your README files directly in Micron, in which case they will display and render as such in any Nomad Network client. The file browser also supports viewing both rendered and raw Markdown and Micron documents.
Code blocks in Markdown can include language hints for syntax highlighting:
.. code:: text
```python
def hello_world():
print("Hello, Reticulum!")
```
Customizing Templates
=====================
The page node uses a template system that allows complete customization of the generated pages. Templates are stored in the ``~/.rngit/templates/`` directory as Micron files.
The following template files are supported:
- ``base.mu`` - Base template wrapping all pages
- ``front.mu`` - Front page listing all groups
- ``group.mu`` - Group page listing repositories
- ``repo.mu`` - Repository overview page
- ``releases.mu`` - Release list page
- ``release.mu`` - Release details page
- ``tree.mu`` - File browser pages
- ``blob.mu`` - File content display
- ``commits.mu`` - Commit history listing
- ``commit.mu`` - Individual commit detail page
- ``refs.mu`` - Branches and tags listing
- ``stats.mu`` - Statistics page
Templates can include the following variables:
- ``{PAGE_CONTENT}`` - The main content of the page (required)
- ``{NODE_NAME}`` - The configured node name
- ``{NAVIGATION}`` - Breadcrumb navigation links
- ``{VERSION}`` - The rngit version number
- ``{GEN_TIME}`` - Page generation time
**Dynamic Templates**
Templates can be made executable to generate dynamic content. If a template file has the executable bit set, it will be executed and its stdout used as the template content.
**Icon Sets**
By default, the page node uses Nerd Font icons. If you prefer simpler icons or your terminal does not support Nerd Fonts, you can enable Unicode icons instead:
.. code:: text
[pages]
serve_nomadnet = yes
unicode_icons = yes
**Repository Statistics**
When statistics recording is enabled (see the ``record_stats`` configuration option), the page node can display activity charts for each repository. The statistics page shows:
- Total and peak views, fetches and pushes
- Daily activity charts over a 90-day period
- Combined activity visualization
To view statistics, a user must have the ``s`` (stats) permission for the repository. See the Access Configuration section for details on setting permissions.
**Repository Thanks**
The page node includes a "Thanks" feature that allows users to express appreciation for a repository. On each repository page, a "Thanks" link is displayed showing the current thanks count. Clicking this link registers a thank you for the repository.
**Configuration Example**
A complete page node configuration might look like this:
.. code:: text
[rngit]
node_name = My Git Node
announce_interval = 360
record_stats = yes
[repositories]
public = /var/git/public
internal = /var/git/internal
[access]
public = r:all
internal = rw:9710b86ba12c42d1d8f30f74fe509286
[pages]
serve_nomadnet = yes
unicode_icons = no
Release Management
==================
In addition to hosting Git repositories, ``rngit`` provides a complete release management system. This allows you to publish versioned releases with associated artifacts, release notes and metadata. Releases are managed through the ``rngit release`` subcommand, and are also viewable through the Nomad Network page interface.
**The Release Workflow**
Creating a release involves specifying a Git tag and a directory containing build artifacts or other files to distribute. The ``rngit`` client will open your configured ``$EDITOR`` to compose release notes, then upload all artifacts to the remote repository node.
To create a release, specify the tag name and path to artifacts:
.. code:: text
$ rngit release rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo create v1.2.0:./dist
This will:
1. Verify that the tag ``v1.2.0`` exists in the repository
2. Open your editor to write release notes
3. Upload all files from the ``./dist`` directory
4. Publish the release
If no ``$EDITOR`` environment variable is set, ``rngit`` will try to use ``nano``, ``vim`` or ``vi``. The editor will show a template with instructions. Lines starting with ``#`` will be ignored, and if the remaining content is empty after stripping comments, the release creation will be cancelled.
**Release Storage & Structure**
Releases are stored on the node in a directory named ``repo_name.releases`` next to the bare repository. Each release is a subdirectory containing:
- ``META`` - Release metadata in ConfigObj format
- ``RELEASE.md`` or ``RELEASE.mu`` - Release notes
- ``artifacts/`` - All uploaded files
- ``THANKS`` - Appreciation count from users
**Listing Releases**
To view all releases for a repository:
.. code:: text
$ rngit release rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo list
Tag Status Created Objs Notes
------------------------------------------------------------------
v1.2.0 published 2025-01-15 14:32 3 Another release
v1.1.0 published 2024-12-03 09:15 2 Bug fix release
v1.0.0 published 2024-10-20 16:45 2 Initial release
**Viewing Release Details**
To see full information about a specific release:
.. code:: text
$ rngit release rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo view v1.2.0
Release : 0.9.2
Status : published
Created : 2026-05-04 23:53:09
Thanks : 5
Release Notes
=============
Version 1.2.0 release notes...
Artifacts (4)
=============
- myapp-1.2.0.tar.gz (1.5 MB)
- myapp-1.2.0.zip (1.6 MB)
- checksums.txt (256 B)
**Deleting Releases**
To remove a release:
.. code:: text
$ rngit release rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo delete v1.2.0
Are you sure you want to delete release 'v1.2.0'? [y/N]: y
Release v1.2.0 deleted
**Requirements & Validation**
- The specified tag must exist in the remote repository
- You must have ``release`` permission for the repository
- The target artifacts directory must exist and contain at least one file
- Release notes cannot be empty
**Permissions**
Release management requires the ``release`` permission, configured the same way as other repository permissions. In the config file or ``.allowed`` files, use ``rel:target`` to grant release management rights:
.. code:: text
# In .allowed file or config
rel:all # Allow everyone
rel:9710b86... # Allow specific identity
rel:none # Deny everyone
**Nomad Network Interface**
When the Nomad Network page node is enabled, releases are displayed on a dedicated releases page for each repository. Each release is listed with its tag, creation date, artifact count and a preview of the release notes. Clicking a release shows the full details including formatted release notes and a listing of all artifacts with their sizes.
Only releases with ``published`` status are visible through the Nomad Network interface. Draft releases (if supported in future implementations) would only be visible through the command-line interface.
**All Command-Line Options (rngit release)**
.. code:: text
usage: rngit release [-h] [--config CONFIG] [--rnsconfig RNSCONFIG]
[-i PATH] [-v] [-q] [--version]
[repository] [operation] [target]
Reticulum Git Release Manager
positional arguments:
repository URL of remote repository
operation list, view, create or delete
target tag and path to release artifacts directory
options:
-h, --help show this help message and exit
--config CONFIG path to alternative config directory
--rnsconfig RNSCONFIG
path to alternative Reticulum config directory
-i, --identity PATH path to release identity
-v, --verbose
-q, --quiet
--version show program's version number and exit
Work Documents
==============
In addition to releases, ``rngit`` provides a work document management system for tracking tasks, investigations, issues and progress related to repositories. Work documents are stored as structured msgpack data and support threaded updates and comments.
**Listing Work Documents**
To view work documents for a repository:
.. code:: text
$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo list
Active documents
=================
ID Title Author Created Comments
---------------------------------------------------------------------------
1 Implemented new feature 9710b86ba12c4f2e… 2025-01-15 14:32 3
2 Fixed bug in parser 8f3a21c9d84e927b… 2025-01-14 09:15 1
Use ``--scope completed`` to view completed work documents, or ``--scope all`` to see both active and completed.
**Viewing a Work Document**
To view a specific work document with all its comments:
.. code:: text
$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo view -d 1
Implement new feature (active #1)
=================================
Author : 9710b86ba12c42d1d8f30f74fe509286
Status : active
Created : 2026-05-05 15:11:11
Edited : 2026-05-05 18:22:11
Format : markdown
Updates : 0
This work document tracks the implementation of the new feature...
Updates
=======
#1 by 9710b86ba12c42d1d8f30f74fe509286 at 2026-05-05 15:38:37
-------------------------------------------------------------
Initial analysis complete
**Creating Work Documents**
To create a new work document:
.. code:: text
$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo create --title "Investigate performance issue"
This will open your configured ``$EDITOR`` to compose the document content. Save and exit to create the document, or save an empty document to cancel.
**Editing Work Documents**
To edit an existing work document:
.. code:: text
$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo edit -d 1
This fetches the current content, opens it in your editor, and sends any changes back to the node.
**Adding Comments**
To add an update to a work document:
.. code:: text
$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo update -d 1
This opens your editor to compose the update.
**Completing Work Documents**
To mark a work document as completed (moving it from ``active`` to ``completed``):
.. code:: text
$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo complete -d 1
Work document #1 completed
**Activating Work Documents**
To mark a work document as active (moving it from ``completed`` to ``active``):
.. code:: text
$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo activate -d 1
Work document #1 activated
**Deleting Work Documents**
To delete a work document and all its comments:
.. code:: text
$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo delete -id 1
Are you sure you want to delete active work document #1? [y/N]: y
Work document #1 deleted
**Permissions**
Users can view work documents and updates if the have ``read`` permission for the repository. If users have ``read`` and ``interact``, they can also post updates/comments on existing work documents. Work document management requires having ``write`` and ``interact`` permission to the repository. These permissions are configured the same way as any other repository permissions. In the config file or ``.allowed`` files, use ``i:target`` to grant work document interaction rights:
.. code:: text
# In .allowed file or config
i:all # Allow everyone
i:9710b86... # Allow specific identity
i:none # Deny everyone
**Author Verification**
Users can only edit or delete work documents and updates they created. The author is cryptographically verified from the interacting link's ``remote_identity``.
**Storage Format**
Work documents are stored in a ``repo_name.work`` directory next to the repository, containing:
- ``active/`` - Active work documents
- ``completed/`` - Completed work documents
Each document is a numbered directory containing:
- ``root`` - The work document content and metadata (msgpack format)
- ``N`` - Numbered comment files (msgpack format)
**Nomad Network Interface**
When the Nomad Network page node is enabled, work documents are viewable through the web interface. The work page lists all documents with their status, and clicking a document shows its full content and updates.
**All Command-Line Options (rngit work)**
.. code:: text
usage: rngit work [-h] [--config CONFIG] [--rnsconfig RNSCONFIG]
[-i PATH] [--scope SCOPE] [-t TITLE] [-d ID] [-v]
[-q] [--version]
[repository] [operation]
Reticulum Git Work Document Manager
positional arguments:
repository URL of remote repository
operation list, view, create, edit, delete, update or complete
options:
-h, --help show this help message and exit
--config CONFIG path to alternative config directory
--rnsconfig RNSCONFIG
path to alternative Reticulum config directory
-i, --identity PATH path to identity
--scope SCOPE document scope: active, completed or all
-t, --title TITLE document title for create
-d, --id ID document ID
-v, --verbose
-q, --quiet
--version show program's version number and exit
+1
View File
@@ -27,6 +27,7 @@ to participate in the development of Reticulum itself.
hardware
interfaces
networks
git
support
examples
license
+3 -245
View File
@@ -683,258 +683,16 @@ another one, which will be created if it does not already exist
The rngit Utility
=================
The ``rngit`` utility provides full Git repository hosting and interaction over Reticulum. It allows you to host Git repositories on Reticulum nodes, and to interact with remote repositories using standard Git commands through the ``rns://`` URL scheme.
The ``rngit`` utility provides full Git repository hosting and interaction over Reticulum, as well as many other useful features for software development, collaboration and publishing. It allows you to host Git repositories on Reticulum nodes, interact with remote repositories using standard Git commands through the ``rns://`` URL scheme, and to publish software releases.
The system consists of two parts: The ``rngit`` node that hosts repositories, and the ``git-remote-rns`` helper that enables Git to communicate with rngit nodes. As soon as you have RNS installed on your system, you can transparently use Git with Reticulum-hosted repositories just like any other type of remote. Git over Reticulum uses URLs in the following format: ``rns://DESTINATION_HASH/group/repo``.
The system consists of two parts: The ``rngit`` node that hosts and manages repositories, and the ``git-remote-rns`` helper that enables Git to communicate with rngit nodes. As soon as you have RNS installed on your system, you can transparently use Git with Reticulum-hosted repositories just like any other type of remote. Git over Reticulum uses URLs in the following format: ``rns://DESTINATION_HASH/group/repo``.
If you set a branch to track a Reticulum remote as the default upstream, you can simply use ``git`` as you normally would; all commands work transparently and as expected.
.. warning::
**The rngit program is a new addition to RNS!** This functionality was introduced in RNS 1.2.0. While great care has been taken to design a secure, but highly configurable and flexible permission system for allowing many users to interact with many different repositories on a single node, ``rngit`` has not been tested extensively in the wild! Be careful when hosting repositories, especially if they are public or semi-public.
**Usage Examples**
Run ``rngit`` to start a repository node:
.. code:: text
$ rngit
[Notice] Starting Reticulum Git Node...
[Notice] Reticulum Git Node listening on <0d7334d411d00120cbad24edf355fdd2>
On the first run, ``rngit`` will create a default configuration file. You will then need to edit this, to point to your repository locations, configure access permissions, and perform any other necessary configuration.
View your identity and destination hashes:
.. code:: text
$ rngit --print-identity
Git Peer Identity : <959e10e5efc1bd9d97a4083babe51dea>
Repository Node Identity : <153cb870b4665b8c1c348896292b0bad>
Repositories Destination : <0d7334d411d00120cbad24edf355fdd2>
If the page server is enabled, the output will also include the Nomad Network destination hash.
You can run ``rngit`` in service mode with logging to file:
.. code:: text
$ rngit -s
Clone a repository from a remote ``rngit`` node:
.. code:: text
$ git clone rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo
Add a Reticulum remote to an existing repository:
.. code:: text
$ git remote add some_remote rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo
Push changes to the Reticulum remote:
.. code:: text
$ git push some_remote master
Get changes from a remote repository:
.. code:: text
$ git pull rns_remote master
**Repository Structure**
The ``rngit`` node organizes repositories into groups. Each group is a directory containing bare Git repositories. The repository path format is ``group_name/repo_name``. For example, a repository at ``/var/git/public/myrepo`` would be accessible as ``public/myrepo`` via the URL ``rns://DESTINATION_HASH/public/myrepo``.
**Configuration**
The ``rngit`` node configuration file is located at ``~/.rngit/config`` (or ``/etc/rngit/config`` for system-wide installations). The default configuration includes:
- Repository group paths defining where to find bare repositories
- Access permissions for groups and individual repositories
- Announce intervals for network visibility
- Optional statistics recording for repository activity
Access permissions can be configured at the group level in the config file, or per-repository using ``.allowed`` files. Permissions use the format ``permission:target`` where permission is ``r`` (read), ``w`` (write), ``rw`` (read/write), ``c`` (create) or ``s`` (stats) and target is ``all``, ``none``, or a specific identity hash.
The ``s`` (stats) permission allows viewing repository activity statistics, including views, fetches and pushes over time. To enable statistics recording, set ``record_stats = yes`` in the ``[rngit]`` section of the configuration file. You can also exclude specific identities from statistics by adding their hashes to ``stats_ignore_identities``.
Repository-specific ``.allowed`` files can be static text files or executable scripts that output permission rules to stdout. A ``group.allowed`` file in a repository group directory applies to all repositories within that group.
**All Command-Line Options (rngit)**
.. code:: text
usage: rngit.py [-h] [--config CONFIG] [--rnsconfig RNSCONFIG] [-s] [-i] [-v]
[-q] [--version]
Reticulum Git Repository Node
options:
-h, --help show this help message and exit
--config CONFIG path to alternative config directory
--rnsconfig RNSCONFIG
path to alternative Reticulum config directory
-p, --print-identity print identity and destination info and exit
-s, --service rngit is running as a service and should log to file
-i, --interactive drop into interactive shell after initialisation
-v, --verbose increase verbosity
-q, --quiet decrease verbosity
--version show program's version number and exit
**All Command-Line Options (git-remote-rns)**
The ``git-remote-rns`` helper is automatically invoked by Git when interacting with ``rns://`` URLs. It is not typically run directly by users, but accepts the following environment variables for configuration:
- ``RNGIT_CONFIG`` - Path to alternative client configuration directory
- ``RNS_CONFIG`` - Path to alternative Reticulum configuration directory
The client configuration file is located at ``~/.rngit/client_config`` and allows adjusting parameters such as the reference batch size for transfers.
**Serving Pages Over Nomad Network**
In addition to providing Git repository access via the Git remote helper protocol, ``rngit`` can also run a `Nomad Network <https://github.com/markqvist/nomadnet>`_ compatible page node. This allows users to browse repository information, view file contents, inspect commit history and access repository statistics through any Nomad Network client.
When enabled, the page node provides a complete interface to your repositories, with automatic Markdown to Micron conversion, syntax-highlighted code browsing, and detailed commit, diff and statistics views.
**Enabling the Git Page Node**
To enable the page node, add the following to your ``~/.rngit/config`` file:
.. code:: text
[pages]
serve_nomadnet = yes
When the page node is enabled, ``rngit`` will listen on a Nomad Network node destination in addition to the Git repository destination. You can view the destination hash by running:
.. code:: text
$ rngit --print-identity
Git Peer Identity : <959e10e5efc1bd9d97a4083babe51dea>
Repository Node Identity : <153cb870b4665b8c1c348896292b0bad>
Repositories Destination : <0d7334d411d00120cbad24edf355fdd2>
Nomad Network Destination : <50824b711717f97c2fb1166ceddd5ea9>
**Accessing Repository Pages**
Once the page server is running, you can access it from any Nomad Network client by connecting to the Nomad Network destination. The page node provides the following views:
- **Front Page** - Lists all repository groups accessible to your identity
- **Group Page** - Shows all repositories within a group
- **Repository Page** - Displays repository overview, description and README
- **File Browser** - Browse directory trees and view file contents
- **Commits View** - View commit history with pagination
- **Commit Details** - Detailed commit information with file changes and diffs
- **Refs View** - List branches and tags
- **Statistics** - Activity charts showing views, fetches and pushes over time
All pages respect the same permission system used for Git access. If an identity does not have read access to a repository, they will not be able to view its pages.
**Syntax Highlighting**
If the ``pygments`` Python module is installed on your system, the page server will automatically apply syntax highlighting to code files. The highlighting supports a wide range of programming languages and uses a color theme optimized for terminal display.
To enable syntax highlighting, install pygments:
.. code:: text
pip install pygments
**Markdown & Micron Support**
README files and other Markdown documents are automatically converted to Micron markup for display in Nomad Network clients. You can also write your README files directly in Micron, in which case they will display and render as such in any Nomad Network client. The file browser also supports viewing both rendered and raw Markdown and Micron documents.
Code blocks in Markdown can include language hints for syntax highlighting:
.. code:: text
```python
def hello_world():
print("Hello, Reticulum!")
```
**Customizing Templates**
The page server uses a template system that allows complete customization of the generated pages. Templates are stored in the ``~/.rngit/templates/`` directory as Micron files.
The following template files are supported:
- ``base.mu`` - Base template wrapping all pages (must include ``{PAGE_CONTENT}``)
- ``front.mu`` - Front page listing all groups
- ``group.mu`` - Group page listing repositories
- ``repo.mu`` - Repository overview page
- ``tree.mu`` - File browser pages
- ``blob.mu`` - File content display
- ``commits.mu`` - Commit history listing
- ``commit.mu`` - Individual commit detail page
- ``refs.mu`` - Branches and tags listing
- ``stats.mu`` - Statistics page
Templates can include the following variables:
- ``{PAGE_CONTENT}`` - The main content of the page (required)
- ``{NODE_NAME}`` - The configured node name
- ``{NAVIGATION}`` - Breadcrumb navigation links
- ``{VERSION}`` - The rngit version number
- ``{GEN_TIME}`` - Page generation time
**Dynamic Templates**
Templates can be made executable to generate dynamic content. If a template file has the executable bit set, it will be executed and its stdout used as the template content.
**Icon Sets**
By default, the page server uses Nerd Font icons. If you prefer simpler icons or your terminal does not support Nerd Fonts, you can enable Unicode icons instead:
.. code:: text
[pages]
serve_nomadnet = yes
unicode_icons = yes
**Repository Statistics**
When statistics recording is enabled (see the ``record_stats`` configuration option), the page server can display activity charts for each repository. The statistics page shows:
- Total and peak views, fetches and pushes
- Daily activity charts over a 90-day period
- Combined activity visualization
To view statistics, a user must have the ``s`` (stats) permission for the repository. See the Access Configuration section for details on setting permissions.
**Repository Thanks**
The page server includes a "Thanks" feature that allows users to express appreciation for a repository. On each repository page, a "Thanks" link is displayed showing the current thanks count. Clicking this link registers a thank you for the repository.
**Configuration Example**
A complete page server configuration might look like this:
.. code:: text
[rngit]
node_name = My Git Server
announce_interval = 360
record_stats = yes
[repositories]
public = /var/git/public
internal = /var/git/internal
[access]
public = r:all
internal = rw:9710b86ba12c42d1d8f30f74fe509286
[pages]
serve_nomadnet = yes
unicode_icons = no
For the full documentation on the `rngit` system, see the :ref:`Git Over Reticulum<git-main>` chapter of this manual.
The rnx Utility
+1 -1
View File
@@ -1,5 +1,5 @@
const DOCUMENTATION_OPTIONS = {
VERSION: '1.2.1',
VERSION: '1.2.3',
LANGUAGE: 'en',
COLLAPSE_INDEX: false,
BUILDER: 'html',
+5 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Code Examples - Reticulum Network Stack 1.2.1 documentation</title>
<title>Code Examples - Reticulum Network Stack 1.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.1 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.1 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -222,6 +222,7 @@
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="git.html">Git Over Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="license.html">Reticulum License</a></li>
@@ -3663,7 +3664,7 @@ will be fully on-par with natively included interfaces, including all supported
</aside>
</div>
</div><script src="_static/documentation_options.js?v=ca842793"></script>
</div><script src="_static/documentation_options.js?v=590429e0"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+5 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 1.2.1 documentation</title>
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 1.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.1 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.1 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -222,6 +222,7 @@
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="git.html">Git Over Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="license.html">Reticulum License</a></li>
@@ -294,7 +295,7 @@
</aside>
</div>
</div><script src="_static/documentation_options.js?v=ca842793"></script>
</div><script src="_static/documentation_options.js?v=590429e0"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+5 -4
View File
@@ -5,7 +5,7 @@
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#"><link rel="search" title="Search" href="search.html">
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 --><title>Index - Reticulum Network Stack 1.2.1 documentation</title>
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 --><title>Index - Reticulum Network Stack 1.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -178,7 +178,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.1 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -202,7 +202,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.1 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -220,6 +220,7 @@
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="git.html">Git Over Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="license.html">Reticulum License</a></li>
@@ -836,7 +837,7 @@
</aside>
</div>
</div><script src="_static/documentation_options.js?v=ca842793"></script>
</div><script src="_static/documentation_options.js?v=590429e0"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+5 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Getting Started Fast - Reticulum Network Stack 1.2.1 documentation</title>
<title>Getting Started Fast - Reticulum Network Stack 1.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.1 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.1 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -222,6 +222,7 @@
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="git.html">Git Over Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="license.html">Reticulum License</a></li>
@@ -966,7 +967,7 @@ All other available modules will still be loaded when needed.</p>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=ca842793"></script>
</div><script src="_static/documentation_options.js?v=590429e0"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+792
View File
@@ -0,0 +1,792 @@
<!doctype html>
<html class="no-js" lang="en" data-content_root="./">
<head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="index" title="Index" href="genindex.html"><link rel="search" title="Search" href="search.html"><link rel="next" title="Support Reticulum" href="support.html"><link rel="prev" title="Building Networks" href="networks.html">
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Git Over Reticulum - Reticulum Network Stack 1.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=bb3cebc5" />
<style>
body {
--color-code-background: #f2f2f2;
--color-code-foreground: #1e1e1e;
}
@media not print {
body[data-theme="dark"] {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
--color-background-primary: #202b38;
--color-background-secondary: #161f27;
--color-foreground-primary: #dbdbdb;
--color-foreground-secondary: #a9b1ba;
--color-brand-primary: #41adff;
--color-background-hover: #161f27;
--color-api-name: #ffbe85;
--color-api-pre-name: #efae75;
}
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
--color-background-primary: #202b38;
--color-background-secondary: #161f27;
--color-foreground-primary: #dbdbdb;
--color-foreground-secondary: #a9b1ba;
--color-brand-primary: #41adff;
--color-background-hover: #161f27;
--color-api-name: #ffbe85;
--color-api-pre-name: #efae75;
}
}
}
</style></head>
<body>
<script>
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
</script>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-toc" viewBox="0 0 24 24">
<title>Contents</title>
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
</svg>
</symbol>
<symbol id="svg-menu" viewBox="0 0 24 24">
<title>Menu</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</symbol>
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
<title>Expand</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24">
<title>Light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24">
<title>Dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
</symbol>
<symbol id="svg-sun-with-moon" viewBox="0 0 24 24">
<title>Auto light/dark, in light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
class="icon-custom-derived-from-feather-sun-and-tabler-moon">
<path style="opacity: 50%" d="M 5.411 14.504 C 5.471 14.504 5.532 14.504 5.591 14.504 C 3.639 16.319 4.383 19.569 6.931 20.352 C 7.693 20.586 8.512 20.551 9.25 20.252 C 8.023 23.207 4.056 23.725 2.11 21.184 C 0.166 18.642 1.702 14.949 4.874 14.536 C 5.051 14.512 5.231 14.5 5.411 14.5 L 5.411 14.504 Z"/>
<line x1="14.5" y1="3.25" x2="14.5" y2="1.25"/>
<line x1="14.5" y1="15.85" x2="14.5" y2="17.85"/>
<line x1="10.044" y1="5.094" x2="8.63" y2="3.68"/>
<line x1="19" y1="14.05" x2="20.414" y2="15.464"/>
<line x1="8.2" y1="9.55" x2="6.2" y2="9.55"/>
<line x1="20.8" y1="9.55" x2="22.8" y2="9.55"/>
<line x1="10.044" y1="14.006" x2="8.63" y2="15.42"/>
<line x1="19" y1="5.05" x2="20.414" y2="3.636"/>
<circle cx="14.5" cy="9.55" r="3.6"/>
</svg>
</symbol>
<symbol id="svg-moon-with-sun" viewBox="0 0 24 24">
<title>Auto light/dark, in dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
class="icon-custom-derived-from-feather-sun-and-tabler-moon">
<path d="M 8.282 7.007 C 8.385 7.007 8.494 7.007 8.595 7.007 C 5.18 10.184 6.481 15.869 10.942 17.24 C 12.275 17.648 13.706 17.589 15 17.066 C 12.851 22.236 5.91 23.143 2.505 18.696 C -0.897 14.249 1.791 7.786 7.342 7.063 C 7.652 7.021 7.965 7 8.282 7 L 8.282 7.007 Z"/>
<line style="opacity: 50%" x1="18" y1="3.705" x2="18" y2="2.5"/>
<line style="opacity: 50%" x1="18" y1="11.295" x2="18" y2="12.5"/>
<line style="opacity: 50%" x1="15.316" y1="4.816" x2="14.464" y2="3.964"/>
<line style="opacity: 50%" x1="20.711" y1="10.212" x2="21.563" y2="11.063"/>
<line style="opacity: 50%" x1="14.205" y1="7.5" x2="13.001" y2="7.5"/>
<line style="opacity: 50%" x1="21.795" y1="7.5" x2="23" y2="7.5"/>
<line style="opacity: 50%" x1="15.316" y1="10.184" x2="14.464" y2="11.036"/>
<line style="opacity: 50%" x1="20.711" y1="4.789" x2="21.563" y2="3.937"/>
<circle style="opacity: 50%" cx="18" cy="7.5" r="2.169"/>
</svg>
</symbol>
<symbol id="svg-pencil" viewBox="0 0 24 24">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-pencil-code">
<path d="M4 20h4l10.5 -10.5a2.828 2.828 0 1 0 -4 -4l-10.5 10.5v4" />
<path d="M13.5 6.5l4 4" />
<path d="M20 21l2 -2l-2 -2" />
<path d="M17 17l-2 2l2 2" />
</svg>
</symbol>
<symbol id="svg-eye" viewBox="0 0 24 24">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-eye-code">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" />
<path
d="M11.11 17.958c-3.209 -.307 -5.91 -2.293 -8.11 -5.958c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6c-.21 .352 -.427 .688 -.647 1.008" />
<path d="M20 21l2 -2l-2 -2" />
<path d="M17 17l-2 2l2 2" />
</svg>
</symbol>
</svg>
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation" aria-label="Toggle site navigation sidebar">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc" aria-label="Toggle table of contents sidebar">
<label class="overlay sidebar-overlay" for="__navigation"></label>
<label class="overlay toc-overlay" for="__toc"></label>
<a class="skip-to-content muted-link" href="#furo-main-content">Skip to content</a>
<div class="page">
<header class="mobile-header">
<div class="header-left">
<label class="nav-overlay-icon" for="__navigation">
<span class="icon"><svg><use href="#svg-menu"></use></svg></span>
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
<button class="theme-toggle" aria-label="Toggle Light / Dark / Auto color theme">
<svg class="theme-icon-when-auto-light"><use href="#svg-sun-with-moon"></use></svg>
<svg class="theme-icon-when-auto-dark"><use href="#svg-moon-with-sun"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-header-icon" for="__toc">
<span class="icon"><svg><use href="#svg-toc"></use></svg></span>
</label>
</div>
</header>
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="index.html">
<div class="sidebar-logo-container">
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
<input type="hidden" name="check_keywords" value="yes">
<input type="hidden" name="area" value="default">
</form>
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="whatis.html">What is Reticulum?</a></li>
<li class="toctree-l1"><a class="reference internal" href="gettingstartedfast.html">Getting Started Fast</a></li>
<li class="toctree-l1"><a class="reference internal" href="zen.html">Zen of Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="software.html">Programs Using Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="using.html">Using Reticulum on Your System</a></li>
<li class="toctree-l1"><a class="reference internal" href="understanding.html">Understanding Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Git Over Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="license.html">Reticulum License</a></li>
</ul>
<ul>
<li class="toctree-l1"><a class="reference internal" href="reference.html">API Reference</a></li>
</ul>
</div>
</div>
</div>
</div>
</aside>
<div class="main">
<div class="content">
<div class="article-container">
<a href="#" class="back-to-top muted-link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle" aria-label="Toggle Light / Dark / Auto color theme">
<svg class="theme-icon-when-auto-light"><use href="#svg-sun-with-moon"></use></svg>
<svg class="theme-icon-when-auto-dark"><use href="#svg-moon-with-sun"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-content-icon" for="__toc">
<span class="icon"><svg><use href="#svg-toc"></use></svg></span>
</label>
</div>
<article role="main" id="furo-main-content">
<section id="git-over-reticulum">
<span id="git-main"></span><h1>Git Over Reticulum<a class="headerlink" href="#git-over-reticulum" title="Link to this heading"></a></h1>
<p>A set of utilities for distributed collaborative software development and publishing is included in RNS.</p>
<p>The system consists of two parts: The <code class="docutils literal notranslate"><span class="pre">rngit</span></code> node that hosts repositories, and the <code class="docutils literal notranslate"><span class="pre">git-remote-rns</span></code> helper that enables Git to communicate with rngit nodes. As soon as you have RNS installed on your system, you can transparently use Git with Reticulum-hosted repositories just like any other type of remote. Git over Reticulum uses URLs in the following format: <code class="docutils literal notranslate"><span class="pre">rns://DESTINATION_HASH/group/repo</span></code>.</p>
<p>If you set a branch to track a Reticulum remote as the default upstream, you can simply use <code class="docutils literal notranslate"><span class="pre">git</span></code> as you normally would; all commands work transparently and as expected.</p>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p><strong>The rngit program is a new addition to RNS!</strong> This functionality was introduced in RNS 1.2.0. While great care has been taken to design a secure, but highly configurable and flexible permission system for allowing many users to interact with many different repositories on a single node, <code class="docutils literal notranslate"><span class="pre">rngit</span></code> has not been tested extensively in the wild! Be careful when hosting repositories, especially if they are public or semi-public.</p>
</div>
<section id="the-rngit-utility">
<h2>The rngit Utility<a class="headerlink" href="#the-rngit-utility" title="Link to this heading"></a></h2>
<p>The <code class="docutils literal notranslate"><span class="pre">rngit</span></code> utility provides full Git repository hosting and interaction over Reticulum. It allows you to host and manage Git repositories and releases on Reticulum nodes, and to interact with remote repositories using standard Git commands through the <code class="docutils literal notranslate"><span class="pre">rns://</span></code> URL scheme.</p>
<p><strong>Usage Examples</strong></p>
<p>Run <code class="docutils literal notranslate"><span class="pre">rngit</span></code> to start a repository node:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit
[Notice] Starting Reticulum Git Node...
[Notice] Reticulum Git Node listening on &lt;0d7334d411d00120cbad24edf355fdd2&gt;
</pre></div>
</div>
<p>On the first run, <code class="docutils literal notranslate"><span class="pre">rngit</span></code> will create a default configuration file. You will then need to edit this, to point to your repository locations, configure access permissions, and perform any other necessary configuration.</p>
<p>View your identity and destination hashes:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit --print-identity
Git Peer Identity : &lt;959e10e5efc1bd9d97a4083babe51dea&gt;
Repository Node Identity : &lt;153cb870b4665b8c1c348896292b0bad&gt;
Repositories Destination : &lt;0d7334d411d00120cbad24edf355fdd2&gt;
</pre></div>
</div>
<p>If the page node is enabled, the output will also include the Nomad Network destination hash.</p>
<p>You can run <code class="docutils literal notranslate"><span class="pre">rngit</span></code> in service mode with logging to file:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit -s
</pre></div>
</div>
<p>Clone a repository from a remote <code class="docutils literal notranslate"><span class="pre">rngit</span></code> node:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ git clone rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo
</pre></div>
</div>
<p>Add a Reticulum remote to an existing repository:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ git remote add some_remote rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo
</pre></div>
</div>
<p>Push changes to the Reticulum remote:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ git push some_remote master
</pre></div>
</div>
<p>Get changes from a remote repository:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ git pull rns_remote master
</pre></div>
</div>
<p><strong>All Command-Line Options (rngit)</strong></p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rngit.py [-h] [--config CONFIG] [--rnsconfig RNSCONFIG] [-s] [-i] [-v]
[-q] [--version]
Reticulum Git Repository Node
options:
-h, --help show this help message and exit
--config CONFIG path to alternative config directory
--rnsconfig RNSCONFIG
path to alternative Reticulum config directory
-p, --print-identity print identity and destination info and exit
-s, --service rngit is running as a service and should log to file
-i, --interactive drop into interactive shell after initialisation
-v, --verbose increase verbosity
-q, --quiet decrease verbosity
--version show program&#39;s version number and exit
</pre></div>
</div>
<p><strong>All Command-Line Options (git-remote-rns)</strong></p>
<p>The <code class="docutils literal notranslate"><span class="pre">git-remote-rns</span></code> helper is automatically invoked by Git when interacting with <code class="docutils literal notranslate"><span class="pre">rns://</span></code> URLs. It is not typically run directly by users, but accepts the following environment variables for configuration:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">RNGIT_CONFIG</span></code> - Path to alternative client configuration directory</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">RNS_CONFIG</span></code> - Path to alternative Reticulum configuration directory</p></li>
</ul>
<p>The client configuration file is located at <code class="docutils literal notranslate"><span class="pre">~/.rngit/client_config</span></code> and allows adjusting parameters such as the reference batch size for transfers.</p>
</section>
<section id="repository-structure">
<h2>Repository Structure<a class="headerlink" href="#repository-structure" title="Link to this heading"></a></h2>
<p>The <code class="docutils literal notranslate"><span class="pre">rngit</span></code> node organizes repositories into groups. Each group is a directory containing bare Git repositories. The repository path format is <code class="docutils literal notranslate"><span class="pre">group_name/repo_name</span></code>. For example, a repository at <code class="docutils literal notranslate"><span class="pre">/var/git/public/myrepo</span></code> would be accessible as <code class="docutils literal notranslate"><span class="pre">public/myrepo</span></code> via the URL <code class="docutils literal notranslate"><span class="pre">rns://DESTINATION_HASH/public/myrepo</span></code>.</p>
<p><strong>Configuration</strong></p>
<p>The <code class="docutils literal notranslate"><span class="pre">rngit</span></code> node configuration file is located at <code class="docutils literal notranslate"><span class="pre">~/.rngit/config</span></code> (or <code class="docutils literal notranslate"><span class="pre">/etc/rngit/config</span></code> for system-wide installations). The default configuration includes:</p>
<ul class="simple">
<li><p>Repository group paths defining where to find bare repositories</p></li>
<li><p>Access permissions for groups and individual repositories</p></li>
<li><p>Announce intervals for network visibility</p></li>
<li><p>Optional statistics recording for repository activity</p></li>
</ul>
<p>Access permissions can be configured at the group level in the config file, or per-repository using <code class="docutils literal notranslate"><span class="pre">.allowed</span></code> files. Permissions use the format <code class="docutils literal notranslate"><span class="pre">permission:target</span></code> where permission is <code class="docutils literal notranslate"><span class="pre">r</span></code> (read), <code class="docutils literal notranslate"><span class="pre">w</span></code> (write), <code class="docutils literal notranslate"><span class="pre">rw</span></code> (read/write), <code class="docutils literal notranslate"><span class="pre">c</span></code> (create) or <code class="docutils literal notranslate"><span class="pre">s</span></code> (stats) and target is <code class="docutils literal notranslate"><span class="pre">all</span></code>, <code class="docutils literal notranslate"><span class="pre">none</span></code>, or a specific identity hash.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">s</span></code> (stats) permission allows viewing repository activity statistics, including views, fetches and pushes over time. To enable statistics recording, set <code class="docutils literal notranslate"><span class="pre">record_stats</span> <span class="pre">=</span> <span class="pre">yes</span></code> in the <code class="docutils literal notranslate"><span class="pre">[rngit]</span></code> section of the configuration file. You can also exclude specific identities from statistics by adding their hashes to <code class="docutils literal notranslate"><span class="pre">stats_ignore_identities</span></code>.</p>
<p>Repository-specific <code class="docutils literal notranslate"><span class="pre">.allowed</span></code> files can be static text files or executable scripts that output permission rules to stdout. A <code class="docutils literal notranslate"><span class="pre">group.allowed</span></code> file in a repository group directory applies to all repositories within that group.</p>
</section>
<section id="serving-pages-over-nomad-network">
<h2>Serving Pages Over Nomad Network<a class="headerlink" href="#serving-pages-over-nomad-network" title="Link to this heading"></a></h2>
<p>In addition to providing Git repository access via the Git remote helper protocol, <code class="docutils literal notranslate"><span class="pre">rngit</span></code> can also run a <a class="reference external" href="https://github.com/markqvist/nomadnet">Nomad Network</a> compatible page node. This allows users to browse repository information, view file contents, inspect commit history and access repository statistics through any Nomad Network client.</p>
<p>When enabled, the page node provides a complete interface to your repositories, with automatic Markdown to Micron conversion, syntax-highlighted code browsing, and detailed commit, diff and statistics views.</p>
<p><strong>Enabling the Git Page Node</strong></p>
<p>To enable the page node, add the following to your <code class="docutils literal notranslate"><span class="pre">~/.rngit/config</span></code> file:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>[pages]
serve_nomadnet = yes
</pre></div>
</div>
<p>When the page node is enabled, <code class="docutils literal notranslate"><span class="pre">rngit</span></code> will listen on a Nomad Network node destination in addition to the Git repository destination. You can view the destination hash by running:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit --print-identity
Git Peer Identity : &lt;959e10e5efc1bd9d97a4083babe51dea&gt;
Repository Node Identity : &lt;153cb870b4665b8c1c348896292b0bad&gt;
Repositories Destination : &lt;0d7334d411d00120cbad24edf355fdd2&gt;
Nomad Network Destination : &lt;50824b711717f97c2fb1166ceddd5ea9&gt;
</pre></div>
</div>
<p><strong>Accessing Repository Pages</strong></p>
<p>Once the page node is running, you can access it from any Nomad Network client by connecting to the Nomad Network destination. The page node provides the following views:</p>
<ul class="simple">
<li><p><strong>Front Page</strong> - Lists all repository groups accessible to your identity</p></li>
<li><p><strong>Group Page</strong> - Shows all repositories within a group</p></li>
<li><p><strong>Repository Page</strong> - Displays repository overview, description and README</p></li>
<li><p><strong>Releases</strong> - List of releases for the repository, with information and downloads</p></li>
<li><p><strong>File Browser</strong> - Browse directory trees and view and download file contents</p></li>
<li><p><strong>Commits View</strong> - View commit history with pagination</p></li>
<li><p><strong>Commit Details</strong> - Detailed commit information with file changes and diffs</p></li>
<li><p><strong>Refs View</strong> - List branches and tags</p></li>
<li><p><strong>Statistics</strong> - Activity charts showing views, fetches and pushes over time</p></li>
</ul>
<p>All pages respect the same permission system used for Git access. If an identity does not have read access to a repository, they will not be able to view its pages.</p>
</section>
<section id="formatting-syntax-highlighting">
<h2>Formatting &amp; Syntax Highlighting<a class="headerlink" href="#formatting-syntax-highlighting" title="Link to this heading"></a></h2>
<p>If the <code class="docutils literal notranslate"><span class="pre">pygments</span></code> Python module is installed on your system, the page node will automatically apply syntax highlighting to code files. The highlighting supports a wide range of programming languages and uses a color theme optimized for terminal display.</p>
<p>To enable syntax highlighting, install pygments:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>pip install pygments
</pre></div>
</div>
<p><strong>Markdown &amp; Micron Support</strong></p>
<p>README files and other Markdown documents are automatically converted to Micron markup for display in Nomad Network clients. You can also write your README files directly in Micron, in which case they will display and render as such in any Nomad Network client. The file browser also supports viewing both rendered and raw Markdown and Micron documents.</p>
<p>Code blocks in Markdown can include language hints for syntax highlighting:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>```python
def hello_world():
print(&quot;Hello, Reticulum!&quot;)
```
</pre></div>
</div>
</section>
<section id="customizing-templates">
<h2>Customizing Templates<a class="headerlink" href="#customizing-templates" title="Link to this heading"></a></h2>
<p>The page node uses a template system that allows complete customization of the generated pages. Templates are stored in the <code class="docutils literal notranslate"><span class="pre">~/.rngit/templates/</span></code> directory as Micron files.</p>
<p>The following template files are supported:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">base.mu</span></code> - Base template wrapping all pages</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">front.mu</span></code> - Front page listing all groups</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">group.mu</span></code> - Group page listing repositories</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">repo.mu</span></code> - Repository overview page</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">releases.mu</span></code> - Release list page</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">release.mu</span></code> - Release details page</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">tree.mu</span></code> - File browser pages</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">blob.mu</span></code> - File content display</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">commits.mu</span></code> - Commit history listing</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">commit.mu</span></code> - Individual commit detail page</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">refs.mu</span></code> - Branches and tags listing</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">stats.mu</span></code> - Statistics page</p></li>
</ul>
<p>Templates can include the following variables:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">{PAGE_CONTENT}</span></code> - The main content of the page (required)</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">{NODE_NAME}</span></code> - The configured node name</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">{NAVIGATION}</span></code> - Breadcrumb navigation links</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">{VERSION}</span></code> - The rngit version number</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">{GEN_TIME}</span></code> - Page generation time</p></li>
</ul>
<p><strong>Dynamic Templates</strong></p>
<p>Templates can be made executable to generate dynamic content. If a template file has the executable bit set, it will be executed and its stdout used as the template content.</p>
<p><strong>Icon Sets</strong></p>
<p>By default, the page node uses Nerd Font icons. If you prefer simpler icons or your terminal does not support Nerd Fonts, you can enable Unicode icons instead:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>[pages]
serve_nomadnet = yes
unicode_icons = yes
</pre></div>
</div>
<p><strong>Repository Statistics</strong></p>
<p>When statistics recording is enabled (see the <code class="docutils literal notranslate"><span class="pre">record_stats</span></code> configuration option), the page node can display activity charts for each repository. The statistics page shows:</p>
<ul class="simple">
<li><p>Total and peak views, fetches and pushes</p></li>
<li><p>Daily activity charts over a 90-day period</p></li>
<li><p>Combined activity visualization</p></li>
</ul>
<p>To view statistics, a user must have the <code class="docutils literal notranslate"><span class="pre">s</span></code> (stats) permission for the repository. See the Access Configuration section for details on setting permissions.</p>
<p><strong>Repository Thanks</strong></p>
<p>The page node includes a “Thanks” feature that allows users to express appreciation for a repository. On each repository page, a “Thanks” link is displayed showing the current thanks count. Clicking this link registers a thank you for the repository.</p>
<p><strong>Configuration Example</strong></p>
<p>A complete page node configuration might look like this:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>[rngit]
node_name = My Git Node
announce_interval = 360
record_stats = yes
[repositories]
public = /var/git/public
internal = /var/git/internal
[access]
public = r:all
internal = rw:9710b86ba12c42d1d8f30f74fe509286
[pages]
serve_nomadnet = yes
unicode_icons = no
</pre></div>
</div>
</section>
<section id="release-management">
<h2>Release Management<a class="headerlink" href="#release-management" title="Link to this heading"></a></h2>
<p>In addition to hosting Git repositories, <code class="docutils literal notranslate"><span class="pre">rngit</span></code> provides a complete release management system. This allows you to publish versioned releases with associated artifacts, release notes and metadata. Releases are managed through the <code class="docutils literal notranslate"><span class="pre">rngit</span> <span class="pre">release</span></code> subcommand, and are also viewable through the Nomad Network page interface.</p>
<p><strong>The Release Workflow</strong></p>
<p>Creating a release involves specifying a Git tag and a directory containing build artifacts or other files to distribute. The <code class="docutils literal notranslate"><span class="pre">rngit</span></code> client will open your configured <code class="docutils literal notranslate"><span class="pre">$EDITOR</span></code> to compose release notes, then upload all artifacts to the remote repository node.</p>
<p>To create a release, specify the tag name and path to artifacts:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit release rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo create v1.2.0:./dist
</pre></div>
</div>
<p>This will:</p>
<ol class="arabic simple">
<li><p>Verify that the tag <code class="docutils literal notranslate"><span class="pre">v1.2.0</span></code> exists in the repository</p></li>
<li><p>Open your editor to write release notes</p></li>
<li><p>Upload all files from the <code class="docutils literal notranslate"><span class="pre">./dist</span></code> directory</p></li>
<li><p>Publish the release</p></li>
</ol>
<p>If no <code class="docutils literal notranslate"><span class="pre">$EDITOR</span></code> environment variable is set, <code class="docutils literal notranslate"><span class="pre">rngit</span></code> will try to use <code class="docutils literal notranslate"><span class="pre">nano</span></code>, <code class="docutils literal notranslate"><span class="pre">vim</span></code> or <code class="docutils literal notranslate"><span class="pre">vi</span></code>. The editor will show a template with instructions. Lines starting with <code class="docutils literal notranslate"><span class="pre">#</span></code> will be ignored, and if the remaining content is empty after stripping comments, the release creation will be cancelled.</p>
<p><strong>Release Storage &amp; Structure</strong></p>
<p>Releases are stored on the node in a directory named <code class="docutils literal notranslate"><span class="pre">repo_name.releases</span></code> next to the bare repository. Each release is a subdirectory containing:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">META</span></code> - Release metadata in ConfigObj format</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">RELEASE.md</span></code> or <code class="docutils literal notranslate"><span class="pre">RELEASE.mu</span></code> - Release notes</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">artifacts/</span></code> - All uploaded files</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">THANKS</span></code> - Appreciation count from users</p></li>
</ul>
<p><strong>Listing Releases</strong></p>
<p>To view all releases for a repository:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit release rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo list
Tag Status Created Objs Notes
------------------------------------------------------------------
v1.2.0 published 2025-01-15 14:32 3 Another release
v1.1.0 published 2024-12-03 09:15 2 Bug fix release
v1.0.0 published 2024-10-20 16:45 2 Initial release
</pre></div>
</div>
<p><strong>Viewing Release Details</strong></p>
<p>To see full information about a specific release:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit release rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo view v1.2.0
Release : 0.9.2
Status : published
Created : 2026-05-04 23:53:09
Thanks : 5
Release Notes
=============
Version 1.2.0 release notes...
Artifacts (4)
=============
- myapp-1.2.0.tar.gz (1.5 MB)
- myapp-1.2.0.zip (1.6 MB)
- checksums.txt (256 B)
</pre></div>
</div>
<p><strong>Deleting Releases</strong></p>
<p>To remove a release:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit release rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo delete v1.2.0
Are you sure you want to delete release &#39;v1.2.0&#39;? [y/N]: y
Release v1.2.0 deleted
</pre></div>
</div>
<p><strong>Requirements &amp; Validation</strong></p>
<ul class="simple">
<li><p>The specified tag must exist in the remote repository</p></li>
<li><p>You must have <code class="docutils literal notranslate"><span class="pre">release</span></code> permission for the repository</p></li>
<li><p>The target artifacts directory must exist and contain at least one file</p></li>
<li><p>Release notes cannot be empty</p></li>
</ul>
<p><strong>Permissions</strong></p>
<p>Release management requires the <code class="docutils literal notranslate"><span class="pre">release</span></code> permission, configured the same way as other repository permissions. In the config file or <code class="docutils literal notranslate"><span class="pre">.allowed</span></code> files, use <code class="docutils literal notranslate"><span class="pre">rel:target</span></code> to grant release management rights:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span># In .allowed file or config
rel:all # Allow everyone
rel:9710b86... # Allow specific identity
rel:none # Deny everyone
</pre></div>
</div>
<p><strong>Nomad Network Interface</strong></p>
<p>When the Nomad Network page node is enabled, releases are displayed on a dedicated releases page for each repository. Each release is listed with its tag, creation date, artifact count and a preview of the release notes. Clicking a release shows the full details including formatted release notes and a listing of all artifacts with their sizes.</p>
<p>Only releases with <code class="docutils literal notranslate"><span class="pre">published</span></code> status are visible through the Nomad Network interface. Draft releases (if supported in future implementations) would only be visible through the command-line interface.</p>
<p><strong>All Command-Line Options (rngit release)</strong></p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rngit release [-h] [--config CONFIG] [--rnsconfig RNSCONFIG]
[-i PATH] [-v] [-q] [--version]
[repository] [operation] [target]
Reticulum Git Release Manager
positional arguments:
repository URL of remote repository
operation list, view, create or delete
target tag and path to release artifacts directory
options:
-h, --help show this help message and exit
--config CONFIG path to alternative config directory
--rnsconfig RNSCONFIG
path to alternative Reticulum config directory
-i, --identity PATH path to release identity
-v, --verbose
-q, --quiet
--version show program&#39;s version number and exit
</pre></div>
</div>
</section>
<section id="work-documents">
<h2>Work Documents<a class="headerlink" href="#work-documents" title="Link to this heading"></a></h2>
<p>In addition to releases, <code class="docutils literal notranslate"><span class="pre">rngit</span></code> provides a work document management system for tracking tasks, investigations, issues and progress related to repositories. Work documents are stored as structured msgpack data and support threaded updates and comments.</p>
<p><strong>Listing Work Documents</strong></p>
<p>To view work documents for a repository:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo list
Active documents
=================
ID Title Author Created Comments
---------------------------------------------------------------------------
1 Implemented new feature 9710b86ba12c4f2e… 2025-01-15 14:32 3
2 Fixed bug in parser 8f3a21c9d84e927b… 2025-01-14 09:15 1
</pre></div>
</div>
<p>Use <code class="docutils literal notranslate"><span class="pre">--scope</span> <span class="pre">completed</span></code> to view completed work documents, or <code class="docutils literal notranslate"><span class="pre">--scope</span> <span class="pre">all</span></code> to see both active and completed.</p>
<p><strong>Viewing a Work Document</strong></p>
<p>To view a specific work document with all its comments:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo view -d 1
Implement new feature (active #1)
=================================
Author : 9710b86ba12c42d1d8f30f74fe509286
Status : active
Created : 2026-05-05 15:11:11
Edited : 2026-05-05 18:22:11
Format : markdown
Updates : 0
This work document tracks the implementation of the new feature...
Updates
=======
#1 by 9710b86ba12c42d1d8f30f74fe509286 at 2026-05-05 15:38:37
-------------------------------------------------------------
Initial analysis complete
</pre></div>
</div>
<p><strong>Creating Work Documents</strong></p>
<p>To create a new work document:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo create --title &quot;Investigate performance issue&quot;
</pre></div>
</div>
<p>This will open your configured <code class="docutils literal notranslate"><span class="pre">$EDITOR</span></code> to compose the document content. Save and exit to create the document, or save an empty document to cancel.</p>
<p><strong>Editing Work Documents</strong></p>
<p>To edit an existing work document:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo edit -d 1
</pre></div>
</div>
<p>This fetches the current content, opens it in your editor, and sends any changes back to the node.</p>
<p><strong>Adding Comments</strong></p>
<p>To add an update to a work document:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo update -d 1
</pre></div>
</div>
<p>This opens your editor to compose the update.</p>
<p><strong>Completing Work Documents</strong></p>
<p>To mark a work document as completed (moving it from <code class="docutils literal notranslate"><span class="pre">active</span></code> to <code class="docutils literal notranslate"><span class="pre">completed</span></code>):</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo complete -d 1
Work document #1 completed
</pre></div>
</div>
<p><strong>Activating Work Documents</strong></p>
<p>To mark a work document as active (moving it from <code class="docutils literal notranslate"><span class="pre">completed</span></code> to <code class="docutils literal notranslate"><span class="pre">active</span></code>):</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo activate -d 1
Work document #1 activated
</pre></div>
</div>
<p><strong>Deleting Work Documents</strong></p>
<p>To delete a work document and all its comments:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo delete -id 1
Are you sure you want to delete active work document #1? [y/N]: y
Work document #1 deleted
</pre></div>
</div>
<p><strong>Permissions</strong></p>
<p>Users can view work documents and updates if the have <code class="docutils literal notranslate"><span class="pre">read</span></code> permission for the repository. If users have <code class="docutils literal notranslate"><span class="pre">read</span></code> and <code class="docutils literal notranslate"><span class="pre">interact</span></code>, they can also post updates/comments on existing work documents. Work document management requires having <code class="docutils literal notranslate"><span class="pre">write</span></code> and <code class="docutils literal notranslate"><span class="pre">interact</span></code> permission to the repository. These permissions are configured the same way as any other repository permissions. In the config file or <code class="docutils literal notranslate"><span class="pre">.allowed</span></code> files, use <code class="docutils literal notranslate"><span class="pre">i:target</span></code> to grant work document interaction rights:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span># In .allowed file or config
i:all # Allow everyone
i:9710b86... # Allow specific identity
i:none # Deny everyone
</pre></div>
</div>
<p><strong>Author Verification</strong></p>
<p>Users can only edit or delete work documents and updates they created. The author is cryptographically verified from the interacting links <code class="docutils literal notranslate"><span class="pre">remote_identity</span></code>.</p>
<p><strong>Storage Format</strong></p>
<p>Work documents are stored in a <code class="docutils literal notranslate"><span class="pre">repo_name.work</span></code> directory next to the repository, containing:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">active/</span></code> - Active work documents</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">completed/</span></code> - Completed work documents</p></li>
</ul>
<p>Each document is a numbered directory containing:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">root</span></code> - The work document content and metadata (msgpack format)</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">N</span></code> - Numbered comment files (msgpack format)</p></li>
</ul>
<p><strong>Nomad Network Interface</strong></p>
<p>When the Nomad Network page node is enabled, work documents are viewable through the web interface. The work page lists all documents with their status, and clicking a document shows its full content and updates.</p>
<p><strong>All Command-Line Options (rngit work)</strong></p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rngit work [-h] [--config CONFIG] [--rnsconfig RNSCONFIG]
[-i PATH] [--scope SCOPE] [-t TITLE] [-d ID] [-v]
[-q] [--version]
[repository] [operation]
Reticulum Git Work Document Manager
positional arguments:
repository URL of remote repository
operation list, view, create, edit, delete, update or complete
options:
-h, --help show this help message and exit
--config CONFIG path to alternative config directory
--rnsconfig RNSCONFIG
path to alternative Reticulum config directory
-i, --identity PATH path to identity
--scope SCOPE document scope: active, completed or all
-t, --title TITLE document title for create
-d, --id ID document ID
-v, --verbose
-q, --quiet
--version show program&#39;s version number and exit
</pre></div>
</div>
</section>
</section>
</article>
</div>
<footer>
<div class="related-pages">
<a class="next-page" href="support.html">
<div class="page-info">
<div class="context">
<span>Next</span>
</div>
<div class="title">Support Reticulum</div>
</div>
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
</a>
<a class="prev-page" href="networks.html">
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
<div class="page-info">
<div class="context">
<span>Previous</span>
</div>
<div class="title">Building Networks</div>
</div>
</a>
</div>
<div class="bottom-of-page">
<div class="left-details">
<div class="copyright">
Copyright &#169; 2025, Mark Qvist
</div>
Generated with <a href="https://www.sphinx-doc.org/">Sphinx</a> and
<a href="https://github.com/pradyunsg/furo">Furo</a>
</div>
<div class="right-details">
</div>
</div>
</footer>
</div>
<aside class="toc-drawer">
<div class="toc-sticky toc-scroll">
<div class="toc-title-container">
<span class="toc-title">
On this page
</span>
</div>
<div class="toc-tree-container">
<div class="toc-tree">
<ul>
<li><a class="reference internal" href="#">Git Over Reticulum</a><ul>
<li><a class="reference internal" href="#the-rngit-utility">The rngit Utility</a></li>
<li><a class="reference internal" href="#repository-structure">Repository Structure</a></li>
<li><a class="reference internal" href="#serving-pages-over-nomad-network">Serving Pages Over Nomad Network</a></li>
<li><a class="reference internal" href="#formatting-syntax-highlighting">Formatting &amp; Syntax Highlighting</a></li>
<li><a class="reference internal" href="#customizing-templates">Customizing Templates</a></li>
<li><a class="reference internal" href="#release-management">Release Management</a></li>
<li><a class="reference internal" href="#work-documents">Work Documents</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=590429e0"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=f281be69"></script>
</body>
</html>
+5 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Communications Hardware - Reticulum Network Stack 1.2.1 documentation</title>
<title>Communications Hardware - Reticulum Network Stack 1.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.1 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.1 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -222,6 +222,7 @@
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="git.html">Git Over Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="license.html">Reticulum License</a></li>
@@ -674,7 +675,7 @@ can be used with Reticulum. This includes virtual software modems such as
</aside>
</div>
</div><script src="_static/documentation_options.js?v=ca842793"></script>
</div><script src="_static/documentation_options.js?v=590429e0"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+15 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Reticulum Network Stack 1.2.1 documentation</title>
<title>Reticulum Network Stack 1.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="#"><div class="brand">Reticulum Network Stack 1.2.1 documentation</div></a>
<a href="#"><div class="brand">Reticulum Network Stack 1.2.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.1 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -222,6 +222,7 @@
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="git.html">Git Over Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="license.html">Reticulum License</a></li>
@@ -523,6 +524,16 @@ to participate in the development of Reticulum itself.</p>
</li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="git.html">Git Over Reticulum</a><ul>
<li class="toctree-l2"><a class="reference internal" href="git.html#the-rngit-utility">The rngit Utility</a></li>
<li class="toctree-l2"><a class="reference internal" href="git.html#repository-structure">Repository Structure</a></li>
<li class="toctree-l2"><a class="reference internal" href="git.html#serving-pages-over-nomad-network">Serving Pages Over Nomad Network</a></li>
<li class="toctree-l2"><a class="reference internal" href="git.html#formatting-syntax-highlighting">Formatting &amp; Syntax Highlighting</a></li>
<li class="toctree-l2"><a class="reference internal" href="git.html#customizing-templates">Customizing Templates</a></li>
<li class="toctree-l2"><a class="reference internal" href="git.html#release-management">Release Management</a></li>
<li class="toctree-l2"><a class="reference internal" href="git.html#work-documents">Work Documents</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a><ul>
<li class="toctree-l2"><a class="reference internal" href="support.html#donations">Donations</a></li>
<li class="toctree-l2"><a class="reference internal" href="support.html#provide-feedback">Provide Feedback</a></li>
@@ -633,7 +644,7 @@ to participate in the development of Reticulum itself.</p>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=ca842793"></script>
</div><script src="_static/documentation_options.js?v=590429e0"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+5 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Configuring Interfaces - Reticulum Network Stack 1.2.1 documentation</title>
<title>Configuring Interfaces - Reticulum Network Stack 1.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.1 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.1 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -222,6 +222,7 @@
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="git.html">Git Over Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="license.html">Reticulum License</a></li>
@@ -1684,7 +1685,7 @@ to <code class="docutils literal notranslate"><span class="pre">30</span></code>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=ca842793"></script>
</div><script src="_static/documentation_options.js?v=590429e0"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+5 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Reticulum License - Reticulum Network Stack 1.2.1 documentation</title>
<title>Reticulum License - Reticulum Network Stack 1.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.1 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.1 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -222,6 +222,7 @@
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="git.html">Git Over Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Reticulum License</a></li>
@@ -343,7 +344,7 @@ SOFTWARE.
</aside>
</div>
</div><script src="_static/documentation_options.js?v=ca842793"></script>
</div><script src="_static/documentation_options.js?v=590429e0"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+8 -7
View File
@@ -3,11 +3,11 @@
<head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="index" title="Index" href="genindex.html"><link rel="search" title="Search" href="search.html"><link rel="next" title="Support Reticulum" href="support.html"><link rel="prev" title="Configuring Interfaces" href="interfaces.html">
<link rel="index" title="Index" href="genindex.html"><link rel="search" title="Search" href="search.html"><link rel="next" title="Git Over Reticulum" href="git.html"><link rel="prev" title="Configuring Interfaces" href="interfaces.html">
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Building Networks - Reticulum Network Stack 1.2.1 documentation</title>
<title>Building Networks - Reticulum Network Stack 1.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.1 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.1 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -222,6 +222,7 @@
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="git.html">Git Over Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="license.html">Reticulum License</a></li>
@@ -593,12 +594,12 @@ differently than a mobile device roaming between radio cells.</p>
<footer>
<div class="related-pages">
<a class="next-page" href="support.html">
<a class="next-page" href="git.html">
<div class="page-info">
<div class="context">
<span>Next</span>
</div>
<div class="title">Support Reticulum</div>
<div class="title">Git Over Reticulum</div>
</div>
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
</a>
@@ -662,7 +663,7 @@ differently than a mobile device roaming between radio cells.</p>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=ca842793"></script>
</div><script src="_static/documentation_options.js?v=590429e0"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
Binary file not shown.
+5 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>API Reference - Reticulum Network Stack 1.2.1 documentation</title>
<title>API Reference - Reticulum Network Stack 1.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.1 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.1 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -222,6 +222,7 @@
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="git.html">Git Over Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="license.html">Reticulum License</a></li>
@@ -2472,7 +2473,7 @@ will announce it.</p>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=ca842793"></script>
</div><script src="_static/documentation_options.js?v=590429e0"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+5 -4
View File
@@ -8,7 +8,7 @@
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<meta name="robots" content="noindex" />
<title>Search - Reticulum Network Stack 1.2.1 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<title>Search - Reticulum Network Stack 1.2.3 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?v=8dab3a3b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.1 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.1 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="#" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -222,6 +222,7 @@
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="git.html">Git Over Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="license.html">Reticulum License</a></li>
@@ -302,7 +303,7 @@
</aside>
</div>
</div><script src="_static/documentation_options.js?v=ca842793"></script>
</div><script src="_static/documentation_options.js?v=590429e0"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
File diff suppressed because one or more lines are too long
+5 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Programs Using Reticulum - Reticulum Network Stack 1.2.1 documentation</title>
<title>Programs Using Reticulum - Reticulum Network Stack 1.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.1 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.1 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -222,6 +222,7 @@
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="git.html">Git Over Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="license.html">Reticulum License</a></li>
@@ -533,7 +534,7 @@ using LXMF.</p>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=ca842793"></script>
</div><script src="_static/documentation_options.js?v=590429e0"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+8 -7
View File
@@ -3,11 +3,11 @@
<head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="color-scheme" content="light dark"><meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="index" title="Index" href="genindex.html"><link rel="search" title="Search" href="search.html"><link rel="next" title="Code Examples" href="examples.html"><link rel="prev" title="Building Networks" href="networks.html">
<link rel="index" title="Index" href="genindex.html"><link rel="search" title="Search" href="search.html"><link rel="next" title="Code Examples" href="examples.html"><link rel="prev" title="Git Over Reticulum" href="git.html">
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Support Reticulum - Reticulum Network Stack 1.2.1 documentation</title>
<title>Support Reticulum - Reticulum Network Stack 1.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.1 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.1 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -222,6 +222,7 @@
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="git.html">Git Over Reticulum</a></li>
<li class="toctree-l1 current current-page"><a class="current reference internal" href="#">Support Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="license.html">Reticulum License</a></li>
@@ -327,14 +328,14 @@ circumstances, so we rely on old-fashioned human feedback.</p>
</div>
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
</a>
<a class="prev-page" href="networks.html">
<a class="prev-page" href="git.html">
<svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg>
<div class="page-info">
<div class="context">
<span>Previous</span>
</div>
<div class="title">Building Networks</div>
<div class="title">Git Over Reticulum</div>
</div>
</a>
@@ -381,7 +382,7 @@ circumstances, so we rely on old-fashioned human feedback.</p>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=ca842793"></script>
</div><script src="_static/documentation_options.js?v=590429e0"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+5 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Understanding Reticulum - Reticulum Network Stack 1.2.1 documentation</title>
<title>Understanding Reticulum - Reticulum Network Stack 1.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.1 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.1 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -222,6 +222,7 @@
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="git.html">Git Over Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="license.html">Reticulum License</a></li>
@@ -1336,7 +1337,7 @@ those risks are acceptable to you.</p>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=ca842793"></script>
</div><script src="_static/documentation_options.js?v=590429e0"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+8 -191
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Using Reticulum on Your System - Reticulum Network Stack 1.2.1 documentation</title>
<title>Using Reticulum on Your System - Reticulum Network Stack 1.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.1 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.1 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -222,6 +222,7 @@
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="git.html">Git Over Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="license.html">Reticulum License</a></li>
@@ -852,198 +853,14 @@ options:
</section>
<section id="the-rngit-utility">
<h3>The rngit Utility<a class="headerlink" href="#the-rngit-utility" title="Link to this heading"></a></h3>
<p>The <code class="docutils literal notranslate"><span class="pre">rngit</span></code> utility provides full Git repository hosting and interaction over Reticulum. It allows you to host Git repositories on Reticulum nodes, and to interact with remote repositories using standard Git commands through the <code class="docutils literal notranslate"><span class="pre">rns://</span></code> URL scheme.</p>
<p>The system consists of two parts: The <code class="docutils literal notranslate"><span class="pre">rngit</span></code> node that hosts repositories, and the <code class="docutils literal notranslate"><span class="pre">git-remote-rns</span></code> helper that enables Git to communicate with rngit nodes. As soon as you have RNS installed on your system, you can transparently use Git with Reticulum-hosted repositories just like any other type of remote. Git over Reticulum uses URLs in the following format: <code class="docutils literal notranslate"><span class="pre">rns://DESTINATION_HASH/group/repo</span></code>.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">rngit</span></code> utility provides full Git repository hosting and interaction over Reticulum, as well as many other useful features for software development, collaboration and publishing. It allows you to host Git repositories on Reticulum nodes, interact with remote repositories using standard Git commands through the <code class="docutils literal notranslate"><span class="pre">rns://</span></code> URL scheme, and to publish software releases.</p>
<p>The system consists of two parts: The <code class="docutils literal notranslate"><span class="pre">rngit</span></code> node that hosts and manages repositories, and the <code class="docutils literal notranslate"><span class="pre">git-remote-rns</span></code> helper that enables Git to communicate with rngit nodes. As soon as you have RNS installed on your system, you can transparently use Git with Reticulum-hosted repositories just like any other type of remote. Git over Reticulum uses URLs in the following format: <code class="docutils literal notranslate"><span class="pre">rns://DESTINATION_HASH/group/repo</span></code>.</p>
<p>If you set a branch to track a Reticulum remote as the default upstream, you can simply use <code class="docutils literal notranslate"><span class="pre">git</span></code> as you normally would; all commands work transparently and as expected.</p>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p><strong>The rngit program is a new addition to RNS!</strong> This functionality was introduced in RNS 1.2.0. While great care has been taken to design a secure, but highly configurable and flexible permission system for allowing many users to interact with many different repositories on a single node, <code class="docutils literal notranslate"><span class="pre">rngit</span></code> has not been tested extensively in the wild! Be careful when hosting repositories, especially if they are public or semi-public.</p>
</div>
<p><strong>Usage Examples</strong></p>
<p>Run <code class="docutils literal notranslate"><span class="pre">rngit</span></code> to start a repository node:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit
[Notice] Starting Reticulum Git Node...
[Notice] Reticulum Git Node listening on &lt;0d7334d411d00120cbad24edf355fdd2&gt;
</pre></div>
</div>
<p>On the first run, <code class="docutils literal notranslate"><span class="pre">rngit</span></code> will create a default configuration file. You will then need to edit this, to point to your repository locations, configure access permissions, and perform any other necessary configuration.</p>
<p>View your identity and destination hashes:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit --print-identity
Git Peer Identity : &lt;959e10e5efc1bd9d97a4083babe51dea&gt;
Repository Node Identity : &lt;153cb870b4665b8c1c348896292b0bad&gt;
Repositories Destination : &lt;0d7334d411d00120cbad24edf355fdd2&gt;
</pre></div>
</div>
<p>If the page server is enabled, the output will also include the Nomad Network destination hash.</p>
<p>You can run <code class="docutils literal notranslate"><span class="pre">rngit</span></code> in service mode with logging to file:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit -s
</pre></div>
</div>
<p>Clone a repository from a remote <code class="docutils literal notranslate"><span class="pre">rngit</span></code> node:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ git clone rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo
</pre></div>
</div>
<p>Add a Reticulum remote to an existing repository:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ git remote add some_remote rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo
</pre></div>
</div>
<p>Push changes to the Reticulum remote:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ git push some_remote master
</pre></div>
</div>
<p>Get changes from a remote repository:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ git pull rns_remote master
</pre></div>
</div>
<p><strong>Repository Structure</strong></p>
<p>The <code class="docutils literal notranslate"><span class="pre">rngit</span></code> node organizes repositories into groups. Each group is a directory containing bare Git repositories. The repository path format is <code class="docutils literal notranslate"><span class="pre">group_name/repo_name</span></code>. For example, a repository at <code class="docutils literal notranslate"><span class="pre">/var/git/public/myrepo</span></code> would be accessible as <code class="docutils literal notranslate"><span class="pre">public/myrepo</span></code> via the URL <code class="docutils literal notranslate"><span class="pre">rns://DESTINATION_HASH/public/myrepo</span></code>.</p>
<p><strong>Configuration</strong></p>
<p>The <code class="docutils literal notranslate"><span class="pre">rngit</span></code> node configuration file is located at <code class="docutils literal notranslate"><span class="pre">~/.rngit/config</span></code> (or <code class="docutils literal notranslate"><span class="pre">/etc/rngit/config</span></code> for system-wide installations). The default configuration includes:</p>
<ul class="simple">
<li><p>Repository group paths defining where to find bare repositories</p></li>
<li><p>Access permissions for groups and individual repositories</p></li>
<li><p>Announce intervals for network visibility</p></li>
<li><p>Optional statistics recording for repository activity</p></li>
</ul>
<p>Access permissions can be configured at the group level in the config file, or per-repository using <code class="docutils literal notranslate"><span class="pre">.allowed</span></code> files. Permissions use the format <code class="docutils literal notranslate"><span class="pre">permission:target</span></code> where permission is <code class="docutils literal notranslate"><span class="pre">r</span></code> (read), <code class="docutils literal notranslate"><span class="pre">w</span></code> (write), <code class="docutils literal notranslate"><span class="pre">rw</span></code> (read/write), <code class="docutils literal notranslate"><span class="pre">c</span></code> (create) or <code class="docutils literal notranslate"><span class="pre">s</span></code> (stats) and target is <code class="docutils literal notranslate"><span class="pre">all</span></code>, <code class="docutils literal notranslate"><span class="pre">none</span></code>, or a specific identity hash.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">s</span></code> (stats) permission allows viewing repository activity statistics, including views, fetches and pushes over time. To enable statistics recording, set <code class="docutils literal notranslate"><span class="pre">record_stats</span> <span class="pre">=</span> <span class="pre">yes</span></code> in the <code class="docutils literal notranslate"><span class="pre">[rngit]</span></code> section of the configuration file. You can also exclude specific identities from statistics by adding their hashes to <code class="docutils literal notranslate"><span class="pre">stats_ignore_identities</span></code>.</p>
<p>Repository-specific <code class="docutils literal notranslate"><span class="pre">.allowed</span></code> files can be static text files or executable scripts that output permission rules to stdout. A <code class="docutils literal notranslate"><span class="pre">group.allowed</span></code> file in a repository group directory applies to all repositories within that group.</p>
<p><strong>All Command-Line Options (rngit)</strong></p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>usage: rngit.py [-h] [--config CONFIG] [--rnsconfig RNSCONFIG] [-s] [-i] [-v]
[-q] [--version]
Reticulum Git Repository Node
options:
-h, --help show this help message and exit
--config CONFIG path to alternative config directory
--rnsconfig RNSCONFIG
path to alternative Reticulum config directory
-p, --print-identity print identity and destination info and exit
-s, --service rngit is running as a service and should log to file
-i, --interactive drop into interactive shell after initialisation
-v, --verbose increase verbosity
-q, --quiet decrease verbosity
--version show program&#39;s version number and exit
</pre></div>
</div>
<p><strong>All Command-Line Options (git-remote-rns)</strong></p>
<p>The <code class="docutils literal notranslate"><span class="pre">git-remote-rns</span></code> helper is automatically invoked by Git when interacting with <code class="docutils literal notranslate"><span class="pre">rns://</span></code> URLs. It is not typically run directly by users, but accepts the following environment variables for configuration:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">RNGIT_CONFIG</span></code> - Path to alternative client configuration directory</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">RNS_CONFIG</span></code> - Path to alternative Reticulum configuration directory</p></li>
</ul>
<p>The client configuration file is located at <code class="docutils literal notranslate"><span class="pre">~/.rngit/client_config</span></code> and allows adjusting parameters such as the reference batch size for transfers.</p>
<p><strong>Serving Pages Over Nomad Network</strong></p>
<p>In addition to providing Git repository access via the Git remote helper protocol, <code class="docutils literal notranslate"><span class="pre">rngit</span></code> can also run a <a class="reference external" href="https://github.com/markqvist/nomadnet">Nomad Network</a> compatible page node. This allows users to browse repository information, view file contents, inspect commit history and access repository statistics through any Nomad Network client.</p>
<p>When enabled, the page node provides a complete interface to your repositories, with automatic Markdown to Micron conversion, syntax-highlighted code browsing, and detailed commit, diff and statistics views.</p>
<p><strong>Enabling the Git Page Node</strong></p>
<p>To enable the page node, add the following to your <code class="docutils literal notranslate"><span class="pre">~/.rngit/config</span></code> file:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>[pages]
serve_nomadnet = yes
</pre></div>
</div>
<p>When the page node is enabled, <code class="docutils literal notranslate"><span class="pre">rngit</span></code> will listen on a Nomad Network node destination in addition to the Git repository destination. You can view the destination hash by running:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>$ rngit --print-identity
Git Peer Identity : &lt;959e10e5efc1bd9d97a4083babe51dea&gt;
Repository Node Identity : &lt;153cb870b4665b8c1c348896292b0bad&gt;
Repositories Destination : &lt;0d7334d411d00120cbad24edf355fdd2&gt;
Nomad Network Destination : &lt;50824b711717f97c2fb1166ceddd5ea9&gt;
</pre></div>
</div>
<p><strong>Accessing Repository Pages</strong></p>
<p>Once the page server is running, you can access it from any Nomad Network client by connecting to the Nomad Network destination. The page node provides the following views:</p>
<ul class="simple">
<li><p><strong>Front Page</strong> - Lists all repository groups accessible to your identity</p></li>
<li><p><strong>Group Page</strong> - Shows all repositories within a group</p></li>
<li><p><strong>Repository Page</strong> - Displays repository overview, description and README</p></li>
<li><p><strong>File Browser</strong> - Browse directory trees and view file contents</p></li>
<li><p><strong>Commits View</strong> - View commit history with pagination</p></li>
<li><p><strong>Commit Details</strong> - Detailed commit information with file changes and diffs</p></li>
<li><p><strong>Refs View</strong> - List branches and tags</p></li>
<li><p><strong>Statistics</strong> - Activity charts showing views, fetches and pushes over time</p></li>
</ul>
<p>All pages respect the same permission system used for Git access. If an identity does not have read access to a repository, they will not be able to view its pages.</p>
<p><strong>Syntax Highlighting</strong></p>
<p>If the <code class="docutils literal notranslate"><span class="pre">pygments</span></code> Python module is installed on your system, the page server will automatically apply syntax highlighting to code files. The highlighting supports a wide range of programming languages and uses a color theme optimized for terminal display.</p>
<p>To enable syntax highlighting, install pygments:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>pip install pygments
</pre></div>
</div>
<p><strong>Markdown &amp; Micron Support</strong></p>
<p>README files and other Markdown documents are automatically converted to Micron markup for display in Nomad Network clients. You can also write your README files directly in Micron, in which case they will display and render as such in any Nomad Network client. The file browser also supports viewing both rendered and raw Markdown and Micron documents.</p>
<p>Code blocks in Markdown can include language hints for syntax highlighting:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>```python
def hello_world():
print(&quot;Hello, Reticulum!&quot;)
```
</pre></div>
</div>
<p><strong>Customizing Templates</strong></p>
<p>The page server uses a template system that allows complete customization of the generated pages. Templates are stored in the <code class="docutils literal notranslate"><span class="pre">~/.rngit/templates/</span></code> directory as Micron files.</p>
<p>The following template files are supported:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">base.mu</span></code> - Base template wrapping all pages (must include <code class="docutils literal notranslate"><span class="pre">{PAGE_CONTENT}</span></code>)</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">front.mu</span></code> - Front page listing all groups</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">group.mu</span></code> - Group page listing repositories</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">repo.mu</span></code> - Repository overview page</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">tree.mu</span></code> - File browser pages</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">blob.mu</span></code> - File content display</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">commits.mu</span></code> - Commit history listing</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">commit.mu</span></code> - Individual commit detail page</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">refs.mu</span></code> - Branches and tags listing</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">stats.mu</span></code> - Statistics page</p></li>
</ul>
<p>Templates can include the following variables:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">{PAGE_CONTENT}</span></code> - The main content of the page (required)</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">{NODE_NAME}</span></code> - The configured node name</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">{NAVIGATION}</span></code> - Breadcrumb navigation links</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">{VERSION}</span></code> - The rngit version number</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">{GEN_TIME}</span></code> - Page generation time</p></li>
</ul>
<p><strong>Dynamic Templates</strong></p>
<p>Templates can be made executable to generate dynamic content. If a template file has the executable bit set, it will be executed and its stdout used as the template content.</p>
<p><strong>Icon Sets</strong></p>
<p>By default, the page server uses Nerd Font icons. If you prefer simpler icons or your terminal does not support Nerd Fonts, you can enable Unicode icons instead:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>[pages]
serve_nomadnet = yes
unicode_icons = yes
</pre></div>
</div>
<p><strong>Repository Statistics</strong></p>
<p>When statistics recording is enabled (see the <code class="docutils literal notranslate"><span class="pre">record_stats</span></code> configuration option), the page server can display activity charts for each repository. The statistics page shows:</p>
<ul class="simple">
<li><p>Total and peak views, fetches and pushes</p></li>
<li><p>Daily activity charts over a 90-day period</p></li>
<li><p>Combined activity visualization</p></li>
</ul>
<p>To view statistics, a user must have the <code class="docutils literal notranslate"><span class="pre">s</span></code> (stats) permission for the repository. See the Access Configuration section for details on setting permissions.</p>
<p><strong>Repository Thanks</strong></p>
<p>The page server includes a “Thanks” feature that allows users to express appreciation for a repository. On each repository page, a “Thanks” link is displayed showing the current thanks count. Clicking this link registers a thank you for the repository.</p>
<p><strong>Configuration Example</strong></p>
<p>A complete page server configuration might look like this:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>[rngit]
node_name = My Git Server
announce_interval = 360
record_stats = yes
[repositories]
public = /var/git/public
internal = /var/git/internal
[access]
public = r:all
internal = rw:9710b86ba12c42d1d8f30f74fe509286
[pages]
serve_nomadnet = yes
unicode_icons = no
</pre></div>
</div>
<p>For the full documentation on the <cite>rngit</cite> system, see the <a class="reference internal" href="git.html#git-main"><span class="std std-ref">Git Over Reticulum</span></a> chapter of this manual.</p>
</section>
<section id="the-rnx-utility">
<h3>The rnx Utility<a class="headerlink" href="#the-rnx-utility" title="Link to this heading"></a></h3>
@@ -1818,7 +1635,7 @@ systemctl --user enable rnsd.service
</aside>
</div>
</div><script src="_static/documentation_options.js?v=ca842793"></script>
</div><script src="_static/documentation_options.js?v=590429e0"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+5 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>What is Reticulum? - Reticulum Network Stack 1.2.1 documentation</title>
<title>What is Reticulum? - Reticulum Network Stack 1.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.1 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.1 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -222,6 +222,7 @@
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="git.html">Git Over Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="license.html">Reticulum License</a></li>
@@ -503,7 +504,7 @@ network, and vice versa.</p>
</aside>
</div>
</div><script src="_static/documentation_options.js?v=ca842793"></script>
</div><script src="_static/documentation_options.js?v=590429e0"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+5 -4
View File
@@ -7,7 +7,7 @@
<link rel="prefetch" href="_static/rns_logo_512.png" as="image">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 -->
<title>Zen of Reticulum - Reticulum Network Stack 1.2.1 documentation</title>
<title>Zen of Reticulum - Reticulum Network Stack 1.2.3 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
@@ -180,7 +180,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.1 documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@@ -204,7 +204,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.1 documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
@@ -222,6 +222,7 @@
<li class="toctree-l1"><a class="reference internal" href="hardware.html">Communications Hardware</a></li>
<li class="toctree-l1"><a class="reference internal" href="interfaces.html">Configuring Interfaces</a></li>
<li class="toctree-l1"><a class="reference internal" href="networks.html">Building Networks</a></li>
<li class="toctree-l1"><a class="reference internal" href="git.html">Git Over Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="support.html">Support Reticulum</a></li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Code Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="license.html">Reticulum License</a></li>
@@ -675,7 +676,7 @@ Imagine a messaging app. You write it once. It works on a laptop connected to fi
</aside>
</div>
</div><script src="_static/documentation_options.js?v=ca842793"></script>
</div><script src="_static/documentation_options.js?v=590429e0"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/scripts/furo.js?v=46bd48cc"></script>
+573
View File
@@ -0,0 +1,573 @@
.. _git-main:
******************
Git Over Reticulum
******************
A set of utilities for distributed collaborative software development and publishing is included in RNS.
The system consists of two parts: The ``rngit`` node that hosts repositories, and the ``git-remote-rns`` helper that enables Git to communicate with rngit nodes. As soon as you have RNS installed on your system, you can transparently use Git with Reticulum-hosted repositories just like any other type of remote. Git over Reticulum uses URLs in the following format: ``rns://DESTINATION_HASH/group/repo``.
If you set a branch to track a Reticulum remote as the default upstream, you can simply use ``git`` as you normally would; all commands work transparently and as expected.
.. warning::
**The rngit program is a new addition to RNS!** This functionality was introduced in RNS 1.2.0. While great care has been taken to design a secure, but highly configurable and flexible permission system for allowing many users to interact with many different repositories on a single node, ``rngit`` has not been tested extensively in the wild! Be careful when hosting repositories, especially if they are public or semi-public.
The rngit Utility
=================
The ``rngit`` utility provides full Git repository hosting and interaction over Reticulum. It allows you to host and manage Git repositories and releases on Reticulum nodes, and to interact with remote repositories using standard Git commands through the ``rns://`` URL scheme.
**Usage Examples**
Run ``rngit`` to start a repository node:
.. code:: text
$ rngit
[Notice] Starting Reticulum Git Node...
[Notice] Reticulum Git Node listening on <0d7334d411d00120cbad24edf355fdd2>
On the first run, ``rngit`` will create a default configuration file. You will then need to edit this, to point to your repository locations, configure access permissions, and perform any other necessary configuration.
View your identity and destination hashes:
.. code:: text
$ rngit --print-identity
Git Peer Identity : <959e10e5efc1bd9d97a4083babe51dea>
Repository Node Identity : <153cb870b4665b8c1c348896292b0bad>
Repositories Destination : <0d7334d411d00120cbad24edf355fdd2>
If the page node is enabled, the output will also include the Nomad Network destination hash.
You can run ``rngit`` in service mode with logging to file:
.. code:: text
$ rngit -s
Clone a repository from a remote ``rngit`` node:
.. code:: text
$ git clone rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo
Add a Reticulum remote to an existing repository:
.. code:: text
$ git remote add some_remote rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo
Push changes to the Reticulum remote:
.. code:: text
$ git push some_remote master
Get changes from a remote repository:
.. code:: text
$ git pull rns_remote master
**All Command-Line Options (rngit)**
.. code:: text
usage: rngit.py [-h] [--config CONFIG] [--rnsconfig RNSCONFIG] [-s] [-i] [-v]
[-q] [--version]
Reticulum Git Repository Node
options:
-h, --help show this help message and exit
--config CONFIG path to alternative config directory
--rnsconfig RNSCONFIG
path to alternative Reticulum config directory
-p, --print-identity print identity and destination info and exit
-s, --service rngit is running as a service and should log to file
-i, --interactive drop into interactive shell after initialisation
-v, --verbose increase verbosity
-q, --quiet decrease verbosity
--version show program's version number and exit
**All Command-Line Options (git-remote-rns)**
The ``git-remote-rns`` helper is automatically invoked by Git when interacting with ``rns://`` URLs. It is not typically run directly by users, but accepts the following environment variables for configuration:
- ``RNGIT_CONFIG`` - Path to alternative client configuration directory
- ``RNS_CONFIG`` - Path to alternative Reticulum configuration directory
The client configuration file is located at ``~/.rngit/client_config`` and allows adjusting parameters such as the reference batch size for transfers.
Repository Structure
====================
The ``rngit`` node organizes repositories into groups. Each group is a directory containing bare Git repositories. The repository path format is ``group_name/repo_name``. For example, a repository at ``/var/git/public/myrepo`` would be accessible as ``public/myrepo`` via the URL ``rns://DESTINATION_HASH/public/myrepo``.
**Configuration**
The ``rngit`` node configuration file is located at ``~/.rngit/config`` (or ``/etc/rngit/config`` for system-wide installations). The default configuration includes:
- Repository group paths defining where to find bare repositories
- Access permissions for groups and individual repositories
- Announce intervals for network visibility
- Optional statistics recording for repository activity
Access permissions can be configured at the group level in the config file, or per-repository using ``.allowed`` files. Permissions use the format ``permission:target`` where permission is ``r`` (read), ``w`` (write), ``rw`` (read/write), ``c`` (create) or ``s`` (stats) and target is ``all``, ``none``, or a specific identity hash.
The ``s`` (stats) permission allows viewing repository activity statistics, including views, fetches and pushes over time. To enable statistics recording, set ``record_stats = yes`` in the ``[rngit]`` section of the configuration file. You can also exclude specific identities from statistics by adding their hashes to ``stats_ignore_identities``.
Repository-specific ``.allowed`` files can be static text files or executable scripts that output permission rules to stdout. A ``group.allowed`` file in a repository group directory applies to all repositories within that group.
Serving Pages Over Nomad Network
================================
In addition to providing Git repository access via the Git remote helper protocol, ``rngit`` can also run a `Nomad Network <https://github.com/markqvist/nomadnet>`_ compatible page node. This allows users to browse repository information, view file contents, inspect commit history and access repository statistics through any Nomad Network client.
When enabled, the page node provides a complete interface to your repositories, with automatic Markdown to Micron conversion, syntax-highlighted code browsing, and detailed commit, diff and statistics views.
**Enabling the Git Page Node**
To enable the page node, add the following to your ``~/.rngit/config`` file:
.. code:: text
[pages]
serve_nomadnet = yes
When the page node is enabled, ``rngit`` will listen on a Nomad Network node destination in addition to the Git repository destination. You can view the destination hash by running:
.. code:: text
$ rngit --print-identity
Git Peer Identity : <959e10e5efc1bd9d97a4083babe51dea>
Repository Node Identity : <153cb870b4665b8c1c348896292b0bad>
Repositories Destination : <0d7334d411d00120cbad24edf355fdd2>
Nomad Network Destination : <50824b711717f97c2fb1166ceddd5ea9>
**Accessing Repository Pages**
Once the page node is running, you can access it from any Nomad Network client by connecting to the Nomad Network destination. The page node provides the following views:
- **Front Page** - Lists all repository groups accessible to your identity
- **Group Page** - Shows all repositories within a group
- **Repository Page** - Displays repository overview, description and README
- **Releases** - List of releases for the repository, with information and downloads
- **File Browser** - Browse directory trees and view and download file contents
- **Commits View** - View commit history with pagination
- **Commit Details** - Detailed commit information with file changes and diffs
- **Refs View** - List branches and tags
- **Statistics** - Activity charts showing views, fetches and pushes over time
All pages respect the same permission system used for Git access. If an identity does not have read access to a repository, they will not be able to view its pages.
Formatting & Syntax Highlighting
================================
If the ``pygments`` Python module is installed on your system, the page node will automatically apply syntax highlighting to code files. The highlighting supports a wide range of programming languages and uses a color theme optimized for terminal display.
To enable syntax highlighting, install pygments:
.. code:: text
pip install pygments
**Markdown & Micron Support**
README files and other Markdown documents are automatically converted to Micron markup for display in Nomad Network clients. You can also write your README files directly in Micron, in which case they will display and render as such in any Nomad Network client. The file browser also supports viewing both rendered and raw Markdown and Micron documents.
Code blocks in Markdown can include language hints for syntax highlighting:
.. code:: text
```python
def hello_world():
print("Hello, Reticulum!")
```
Customizing Templates
=====================
The page node uses a template system that allows complete customization of the generated pages. Templates are stored in the ``~/.rngit/templates/`` directory as Micron files.
The following template files are supported:
- ``base.mu`` - Base template wrapping all pages
- ``front.mu`` - Front page listing all groups
- ``group.mu`` - Group page listing repositories
- ``repo.mu`` - Repository overview page
- ``releases.mu`` - Release list page
- ``release.mu`` - Release details page
- ``tree.mu`` - File browser pages
- ``blob.mu`` - File content display
- ``commits.mu`` - Commit history listing
- ``commit.mu`` - Individual commit detail page
- ``refs.mu`` - Branches and tags listing
- ``stats.mu`` - Statistics page
Templates can include the following variables:
- ``{PAGE_CONTENT}`` - The main content of the page (required)
- ``{NODE_NAME}`` - The configured node name
- ``{NAVIGATION}`` - Breadcrumb navigation links
- ``{VERSION}`` - The rngit version number
- ``{GEN_TIME}`` - Page generation time
**Dynamic Templates**
Templates can be made executable to generate dynamic content. If a template file has the executable bit set, it will be executed and its stdout used as the template content.
**Icon Sets**
By default, the page node uses Nerd Font icons. If you prefer simpler icons or your terminal does not support Nerd Fonts, you can enable Unicode icons instead:
.. code:: text
[pages]
serve_nomadnet = yes
unicode_icons = yes
**Repository Statistics**
When statistics recording is enabled (see the ``record_stats`` configuration option), the page node can display activity charts for each repository. The statistics page shows:
- Total and peak views, fetches and pushes
- Daily activity charts over a 90-day period
- Combined activity visualization
To view statistics, a user must have the ``s`` (stats) permission for the repository. See the Access Configuration section for details on setting permissions.
**Repository Thanks**
The page node includes a "Thanks" feature that allows users to express appreciation for a repository. On each repository page, a "Thanks" link is displayed showing the current thanks count. Clicking this link registers a thank you for the repository.
**Configuration Example**
A complete page node configuration might look like this:
.. code:: text
[rngit]
node_name = My Git Node
announce_interval = 360
record_stats = yes
[repositories]
public = /var/git/public
internal = /var/git/internal
[access]
public = r:all
internal = rw:9710b86ba12c42d1d8f30f74fe509286
[pages]
serve_nomadnet = yes
unicode_icons = no
Release Management
==================
In addition to hosting Git repositories, ``rngit`` provides a complete release management system. This allows you to publish versioned releases with associated artifacts, release notes and metadata. Releases are managed through the ``rngit release`` subcommand, and are also viewable through the Nomad Network page interface.
**The Release Workflow**
Creating a release involves specifying a Git tag and a directory containing build artifacts or other files to distribute. The ``rngit`` client will open your configured ``$EDITOR`` to compose release notes, then upload all artifacts to the remote repository node.
To create a release, specify the tag name and path to artifacts:
.. code:: text
$ rngit release rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo create v1.2.0:./dist
This will:
1. Verify that the tag ``v1.2.0`` exists in the repository
2. Open your editor to write release notes
3. Upload all files from the ``./dist`` directory
4. Publish the release
If no ``$EDITOR`` environment variable is set, ``rngit`` will try to use ``nano``, ``vim`` or ``vi``. The editor will show a template with instructions. Lines starting with ``#`` will be ignored, and if the remaining content is empty after stripping comments, the release creation will be cancelled.
**Release Storage & Structure**
Releases are stored on the node in a directory named ``repo_name.releases`` next to the bare repository. Each release is a subdirectory containing:
- ``META`` - Release metadata in ConfigObj format
- ``RELEASE.md`` or ``RELEASE.mu`` - Release notes
- ``artifacts/`` - All uploaded files
- ``THANKS`` - Appreciation count from users
**Listing Releases**
To view all releases for a repository:
.. code:: text
$ rngit release rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo list
Tag Status Created Objs Notes
------------------------------------------------------------------
v1.2.0 published 2025-01-15 14:32 3 Another release
v1.1.0 published 2024-12-03 09:15 2 Bug fix release
v1.0.0 published 2024-10-20 16:45 2 Initial release
**Viewing Release Details**
To see full information about a specific release:
.. code:: text
$ rngit release rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo view v1.2.0
Release : 0.9.2
Status : published
Created : 2026-05-04 23:53:09
Thanks : 5
Release Notes
=============
Version 1.2.0 release notes...
Artifacts (4)
=============
- myapp-1.2.0.tar.gz (1.5 MB)
- myapp-1.2.0.zip (1.6 MB)
- checksums.txt (256 B)
**Deleting Releases**
To remove a release:
.. code:: text
$ rngit release rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo delete v1.2.0
Are you sure you want to delete release 'v1.2.0'? [y/N]: y
Release v1.2.0 deleted
**Requirements & Validation**
- The specified tag must exist in the remote repository
- You must have ``release`` permission for the repository
- The target artifacts directory must exist and contain at least one file
- Release notes cannot be empty
**Permissions**
Release management requires the ``release`` permission, configured the same way as other repository permissions. In the config file or ``.allowed`` files, use ``rel:target`` to grant release management rights:
.. code:: text
# In .allowed file or config
rel:all # Allow everyone
rel:9710b86... # Allow specific identity
rel:none # Deny everyone
**Nomad Network Interface**
When the Nomad Network page node is enabled, releases are displayed on a dedicated releases page for each repository. Each release is listed with its tag, creation date, artifact count and a preview of the release notes. Clicking a release shows the full details including formatted release notes and a listing of all artifacts with their sizes.
Only releases with ``published`` status are visible through the Nomad Network interface. Draft releases (if supported in future implementations) would only be visible through the command-line interface.
**All Command-Line Options (rngit release)**
.. code:: text
usage: rngit release [-h] [--config CONFIG] [--rnsconfig RNSCONFIG]
[-i PATH] [-v] [-q] [--version]
[repository] [operation] [target]
Reticulum Git Release Manager
positional arguments:
repository URL of remote repository
operation list, view, create or delete
target tag and path to release artifacts directory
options:
-h, --help show this help message and exit
--config CONFIG path to alternative config directory
--rnsconfig RNSCONFIG
path to alternative Reticulum config directory
-i, --identity PATH path to release identity
-v, --verbose
-q, --quiet
--version show program's version number and exit
Work Documents
==============
In addition to releases, ``rngit`` provides a work document management system for tracking tasks, investigations, issues and progress related to repositories. Work documents are stored as structured msgpack data and support threaded updates and comments.
**Listing Work Documents**
To view work documents for a repository:
.. code:: text
$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo list
Active documents
=================
ID Title Author Created Comments
---------------------------------------------------------------------------
1 Implemented new feature 9710b86ba12c4f2e… 2025-01-15 14:32 3
2 Fixed bug in parser 8f3a21c9d84e927b… 2025-01-14 09:15 1
Use ``--scope completed`` to view completed work documents, or ``--scope all`` to see both active and completed.
**Viewing a Work Document**
To view a specific work document with all its comments:
.. code:: text
$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo view -d 1
Implement new feature (active #1)
=================================
Author : 9710b86ba12c42d1d8f30f74fe509286
Status : active
Created : 2026-05-05 15:11:11
Edited : 2026-05-05 18:22:11
Format : markdown
Updates : 0
This work document tracks the implementation of the new feature...
Updates
=======
#1 by 9710b86ba12c42d1d8f30f74fe509286 at 2026-05-05 15:38:37
-------------------------------------------------------------
Initial analysis complete
**Creating Work Documents**
To create a new work document:
.. code:: text
$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo create --title "Investigate performance issue"
This will open your configured ``$EDITOR`` to compose the document content. Save and exit to create the document, or save an empty document to cancel.
**Editing Work Documents**
To edit an existing work document:
.. code:: text
$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo edit -d 1
This fetches the current content, opens it in your editor, and sends any changes back to the node.
**Adding Comments**
To add an update to a work document:
.. code:: text
$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo update -d 1
This opens your editor to compose the update.
**Completing Work Documents**
To mark a work document as completed (moving it from ``active`` to ``completed``):
.. code:: text
$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo complete -d 1
Work document #1 completed
**Activating Work Documents**
To mark a work document as active (moving it from ``completed`` to ``active``):
.. code:: text
$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo activate -d 1
Work document #1 activated
**Deleting Work Documents**
To delete a work document and all its comments:
.. code:: text
$ rngit work rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo delete -id 1
Are you sure you want to delete active work document #1? [y/N]: y
Work document #1 deleted
**Permissions**
Users can view work documents and updates if the have ``read`` permission for the repository. If users have ``read`` and ``interact``, they can also post updates/comments on existing work documents. Work document management requires having ``write`` and ``interact`` permission to the repository. These permissions are configured the same way as any other repository permissions. In the config file or ``.allowed`` files, use ``i:target`` to grant work document interaction rights:
.. code:: text
# In .allowed file or config
i:all # Allow everyone
i:9710b86... # Allow specific identity
i:none # Deny everyone
**Author Verification**
Users can only edit or delete work documents and updates they created. The author is cryptographically verified from the interacting link's ``remote_identity``.
**Storage Format**
Work documents are stored in a ``repo_name.work`` directory next to the repository, containing:
- ``active/`` - Active work documents
- ``completed/`` - Completed work documents
Each document is a numbered directory containing:
- ``root`` - The work document content and metadata (msgpack format)
- ``N`` - Numbered comment files (msgpack format)
**Nomad Network Interface**
When the Nomad Network page node is enabled, work documents are viewable through the web interface. The work page lists all documents with their status, and clicking a document shows its full content and updates.
**All Command-Line Options (rngit work)**
.. code:: text
usage: rngit work [-h] [--config CONFIG] [--rnsconfig RNSCONFIG]
[-i PATH] [--scope SCOPE] [-t TITLE] [-d ID] [-v]
[-q] [--version]
[repository] [operation]
Reticulum Git Work Document Manager
positional arguments:
repository URL of remote repository
operation list, view, create, edit, delete, update or complete
options:
-h, --help show this help message and exit
--config CONFIG path to alternative config directory
--rnsconfig RNSCONFIG
path to alternative Reticulum config directory
-i, --identity PATH path to identity
--scope SCOPE document scope: active, completed or all
-t, --title TITLE document title for create
-d, --id ID document ID
-v, --verbose
-q, --quiet
--version show program's version number and exit
+1
View File
@@ -27,6 +27,7 @@ to participate in the development of Reticulum itself.
hardware
interfaces
networks
git
support
examples
license
Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

+3 -245
View File
@@ -683,258 +683,16 @@ another one, which will be created if it does not already exist
The rngit Utility
=================
The ``rngit`` utility provides full Git repository hosting and interaction over Reticulum. It allows you to host Git repositories on Reticulum nodes, and to interact with remote repositories using standard Git commands through the ``rns://`` URL scheme.
The ``rngit`` utility provides full Git repository hosting and interaction over Reticulum, as well as many other useful features for software development, collaboration and publishing. It allows you to host Git repositories on Reticulum nodes, interact with remote repositories using standard Git commands through the ``rns://`` URL scheme, and to publish software releases.
The system consists of two parts: The ``rngit`` node that hosts repositories, and the ``git-remote-rns`` helper that enables Git to communicate with rngit nodes. As soon as you have RNS installed on your system, you can transparently use Git with Reticulum-hosted repositories just like any other type of remote. Git over Reticulum uses URLs in the following format: ``rns://DESTINATION_HASH/group/repo``.
The system consists of two parts: The ``rngit`` node that hosts and manages repositories, and the ``git-remote-rns`` helper that enables Git to communicate with rngit nodes. As soon as you have RNS installed on your system, you can transparently use Git with Reticulum-hosted repositories just like any other type of remote. Git over Reticulum uses URLs in the following format: ``rns://DESTINATION_HASH/group/repo``.
If you set a branch to track a Reticulum remote as the default upstream, you can simply use ``git`` as you normally would; all commands work transparently and as expected.
.. warning::
**The rngit program is a new addition to RNS!** This functionality was introduced in RNS 1.2.0. While great care has been taken to design a secure, but highly configurable and flexible permission system for allowing many users to interact with many different repositories on a single node, ``rngit`` has not been tested extensively in the wild! Be careful when hosting repositories, especially if they are public or semi-public.
**Usage Examples**
Run ``rngit`` to start a repository node:
.. code:: text
$ rngit
[Notice] Starting Reticulum Git Node...
[Notice] Reticulum Git Node listening on <0d7334d411d00120cbad24edf355fdd2>
On the first run, ``rngit`` will create a default configuration file. You will then need to edit this, to point to your repository locations, configure access permissions, and perform any other necessary configuration.
View your identity and destination hashes:
.. code:: text
$ rngit --print-identity
Git Peer Identity : <959e10e5efc1bd9d97a4083babe51dea>
Repository Node Identity : <153cb870b4665b8c1c348896292b0bad>
Repositories Destination : <0d7334d411d00120cbad24edf355fdd2>
If the page server is enabled, the output will also include the Nomad Network destination hash.
You can run ``rngit`` in service mode with logging to file:
.. code:: text
$ rngit -s
Clone a repository from a remote ``rngit`` node:
.. code:: text
$ git clone rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo
Add a Reticulum remote to an existing repository:
.. code:: text
$ git remote add some_remote rns://50824b711717f97c2fb1166ceddd5ea9/public/myrepo
Push changes to the Reticulum remote:
.. code:: text
$ git push some_remote master
Get changes from a remote repository:
.. code:: text
$ git pull rns_remote master
**Repository Structure**
The ``rngit`` node organizes repositories into groups. Each group is a directory containing bare Git repositories. The repository path format is ``group_name/repo_name``. For example, a repository at ``/var/git/public/myrepo`` would be accessible as ``public/myrepo`` via the URL ``rns://DESTINATION_HASH/public/myrepo``.
**Configuration**
The ``rngit`` node configuration file is located at ``~/.rngit/config`` (or ``/etc/rngit/config`` for system-wide installations). The default configuration includes:
- Repository group paths defining where to find bare repositories
- Access permissions for groups and individual repositories
- Announce intervals for network visibility
- Optional statistics recording for repository activity
Access permissions can be configured at the group level in the config file, or per-repository using ``.allowed`` files. Permissions use the format ``permission:target`` where permission is ``r`` (read), ``w`` (write), ``rw`` (read/write), ``c`` (create) or ``s`` (stats) and target is ``all``, ``none``, or a specific identity hash.
The ``s`` (stats) permission allows viewing repository activity statistics, including views, fetches and pushes over time. To enable statistics recording, set ``record_stats = yes`` in the ``[rngit]`` section of the configuration file. You can also exclude specific identities from statistics by adding their hashes to ``stats_ignore_identities``.
Repository-specific ``.allowed`` files can be static text files or executable scripts that output permission rules to stdout. A ``group.allowed`` file in a repository group directory applies to all repositories within that group.
**All Command-Line Options (rngit)**
.. code:: text
usage: rngit.py [-h] [--config CONFIG] [--rnsconfig RNSCONFIG] [-s] [-i] [-v]
[-q] [--version]
Reticulum Git Repository Node
options:
-h, --help show this help message and exit
--config CONFIG path to alternative config directory
--rnsconfig RNSCONFIG
path to alternative Reticulum config directory
-p, --print-identity print identity and destination info and exit
-s, --service rngit is running as a service and should log to file
-i, --interactive drop into interactive shell after initialisation
-v, --verbose increase verbosity
-q, --quiet decrease verbosity
--version show program's version number and exit
**All Command-Line Options (git-remote-rns)**
The ``git-remote-rns`` helper is automatically invoked by Git when interacting with ``rns://`` URLs. It is not typically run directly by users, but accepts the following environment variables for configuration:
- ``RNGIT_CONFIG`` - Path to alternative client configuration directory
- ``RNS_CONFIG`` - Path to alternative Reticulum configuration directory
The client configuration file is located at ``~/.rngit/client_config`` and allows adjusting parameters such as the reference batch size for transfers.
**Serving Pages Over Nomad Network**
In addition to providing Git repository access via the Git remote helper protocol, ``rngit`` can also run a `Nomad Network <https://github.com/markqvist/nomadnet>`_ compatible page node. This allows users to browse repository information, view file contents, inspect commit history and access repository statistics through any Nomad Network client.
When enabled, the page node provides a complete interface to your repositories, with automatic Markdown to Micron conversion, syntax-highlighted code browsing, and detailed commit, diff and statistics views.
**Enabling the Git Page Node**
To enable the page node, add the following to your ``~/.rngit/config`` file:
.. code:: text
[pages]
serve_nomadnet = yes
When the page node is enabled, ``rngit`` will listen on a Nomad Network node destination in addition to the Git repository destination. You can view the destination hash by running:
.. code:: text
$ rngit --print-identity
Git Peer Identity : <959e10e5efc1bd9d97a4083babe51dea>
Repository Node Identity : <153cb870b4665b8c1c348896292b0bad>
Repositories Destination : <0d7334d411d00120cbad24edf355fdd2>
Nomad Network Destination : <50824b711717f97c2fb1166ceddd5ea9>
**Accessing Repository Pages**
Once the page server is running, you can access it from any Nomad Network client by connecting to the Nomad Network destination. The page node provides the following views:
- **Front Page** - Lists all repository groups accessible to your identity
- **Group Page** - Shows all repositories within a group
- **Repository Page** - Displays repository overview, description and README
- **File Browser** - Browse directory trees and view file contents
- **Commits View** - View commit history with pagination
- **Commit Details** - Detailed commit information with file changes and diffs
- **Refs View** - List branches and tags
- **Statistics** - Activity charts showing views, fetches and pushes over time
All pages respect the same permission system used for Git access. If an identity does not have read access to a repository, they will not be able to view its pages.
**Syntax Highlighting**
If the ``pygments`` Python module is installed on your system, the page server will automatically apply syntax highlighting to code files. The highlighting supports a wide range of programming languages and uses a color theme optimized for terminal display.
To enable syntax highlighting, install pygments:
.. code:: text
pip install pygments
**Markdown & Micron Support**
README files and other Markdown documents are automatically converted to Micron markup for display in Nomad Network clients. You can also write your README files directly in Micron, in which case they will display and render as such in any Nomad Network client. The file browser also supports viewing both rendered and raw Markdown and Micron documents.
Code blocks in Markdown can include language hints for syntax highlighting:
.. code:: text
```python
def hello_world():
print("Hello, Reticulum!")
```
**Customizing Templates**
The page server uses a template system that allows complete customization of the generated pages. Templates are stored in the ``~/.rngit/templates/`` directory as Micron files.
The following template files are supported:
- ``base.mu`` - Base template wrapping all pages (must include ``{PAGE_CONTENT}``)
- ``front.mu`` - Front page listing all groups
- ``group.mu`` - Group page listing repositories
- ``repo.mu`` - Repository overview page
- ``tree.mu`` - File browser pages
- ``blob.mu`` - File content display
- ``commits.mu`` - Commit history listing
- ``commit.mu`` - Individual commit detail page
- ``refs.mu`` - Branches and tags listing
- ``stats.mu`` - Statistics page
Templates can include the following variables:
- ``{PAGE_CONTENT}`` - The main content of the page (required)
- ``{NODE_NAME}`` - The configured node name
- ``{NAVIGATION}`` - Breadcrumb navigation links
- ``{VERSION}`` - The rngit version number
- ``{GEN_TIME}`` - Page generation time
**Dynamic Templates**
Templates can be made executable to generate dynamic content. If a template file has the executable bit set, it will be executed and its stdout used as the template content.
**Icon Sets**
By default, the page server uses Nerd Font icons. If you prefer simpler icons or your terminal does not support Nerd Fonts, you can enable Unicode icons instead:
.. code:: text
[pages]
serve_nomadnet = yes
unicode_icons = yes
**Repository Statistics**
When statistics recording is enabled (see the ``record_stats`` configuration option), the page server can display activity charts for each repository. The statistics page shows:
- Total and peak views, fetches and pushes
- Daily activity charts over a 90-day period
- Combined activity visualization
To view statistics, a user must have the ``s`` (stats) permission for the repository. See the Access Configuration section for details on setting permissions.
**Repository Thanks**
The page server includes a "Thanks" feature that allows users to express appreciation for a repository. On each repository page, a "Thanks" link is displayed showing the current thanks count. Clicking this link registers a thank you for the repository.
**Configuration Example**
A complete page server configuration might look like this:
.. code:: text
[rngit]
node_name = My Git Server
announce_interval = 360
record_stats = yes
[repositories]
public = /var/git/public
internal = /var/git/internal
[access]
public = r:all
internal = rw:9710b86ba12c42d1d8f30f74fe509286
[pages]
serve_nomadnet = yes
unicode_icons = no
For the full documentation on the `rngit` system, see the :ref:`Git Over Reticulum<git-main>` chapter of this manual.
The rnx Utility