mirror of
https://github.com/markqvist/Reticulum.git
synced 2026-06-23 20:34:29 -07:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9885a70a88 | |||
| e4a85de089 | |||
| ca0d2dffbe | |||
| 511d169c77 | |||
| 19bc8ef85c | |||
| 1e33d3eebb | |||
| d18f434583 | |||
| 64749b4d18 | |||
| 875d8ef7eb | |||
| 20283f1536 | |||
| c4af328802 | |||
| a2193b9ffd | |||
| d2542fd49b | |||
| 0333884877 | |||
| 63947ed69a | |||
| d6d18ce29c | |||
| 2ef58d8b59 | |||
| 15f2d1635e | |||
| c83b71f49a | |||
| f0824fd71e | |||
| 8dde60658f | |||
| 9437648ae5 | |||
| 71b19aca2c | |||
| 7d320f8cd5 | |||
| 340d0883a7 | |||
| 66096acc29 | |||
| e35100d865 | |||
| 128455ef01 | |||
| 10156cc90e | |||
| 4f5482f2ae | |||
| 2b9fdae74b | |||
| 7506caa0da |
@@ -1,3 +1,32 @@
|
||||
### 2026-05-18: RNS 1.2.8
|
||||
|
||||
This release improves the `rngit` system with signed release manifest generation and automatic artifact signing. It also includes several additions to `rnid` and various minor fixes and improvements to the `rngit` system.
|
||||
|
||||
**Changes**
|
||||
- Added signed release manifest generation to `rngit release`
|
||||
- Added verified release fetching to `rngit release`
|
||||
- Added automatic artifact signing to `rngit release`
|
||||
- Added signed message creation from file to `rnid`
|
||||
- Added signed message metadata output option to `rnid`
|
||||
- Added `rsm` metadata embedding and spec validation to `rnid`
|
||||
- Added identity and destination aliases to `rngit`
|
||||
- Added blocked identities option to `rngit`
|
||||
- Added ability to render raw micron in markdown files to `rngit`
|
||||
- Added fork and mirror last sync time to repository page in `rngit`
|
||||
- Better handling of silly links in `rngit`
|
||||
- Fixed markdown table cell truncation not closing micron tags
|
||||
- Fixed various minor bugs and inconsistencies in `rngit`
|
||||
- Dropped `note` metadata field requirement from `rsg` structure
|
||||
|
||||
**Release Signatures**
|
||||
Release artifacts include a signed `rsm` release manifest and `rsg` signature files that can be validated against the RNS release signing identity `<bc7291552be7a58f361522990465165c>` using `rnid`. To verify files, download the `rsm` and `rsg` signatures, make sure they are in the same folder as the release artifact, and run `rnid` signature verification with the release identity as the required signer:
|
||||
|
||||
```sh
|
||||
rnid -i bc7291552be7a58f361522990465165c -V manifest.rsm *.rsg
|
||||
```
|
||||
|
||||
The `rnid` utility will then verify the signatures, and display whether they are valid. If the signature cannot be verified, the release has been tampered with and should be discarded.
|
||||
|
||||
### 2026-05-17: RNS 1.2.7
|
||||
|
||||
This release significantly improves the `rngit` system with fork, mirroring and empty repository creation functionality, a new work document proposals feature, improvements to the transport core reliability and efficiency and various other tweaks and improvements.
|
||||
|
||||
@@ -101,6 +101,7 @@ class ReticulumGitClient():
|
||||
self.config = None
|
||||
self.ready = False
|
||||
|
||||
self.destination_aliases = {}
|
||||
self.remote_identity = None
|
||||
self.destination = None
|
||||
self.link = None
|
||||
@@ -170,6 +171,18 @@ class ReticulumGitClient():
|
||||
section = self.config["client"]
|
||||
if "ref_batch_size" in section: self.ref_batch_size = max(0, min(1024, section.as_int("ref_batch_size")))
|
||||
|
||||
if "aliases" in self.config:
|
||||
section = self.config["aliases"]
|
||||
for alias in section:
|
||||
alias_hexhash = section[alias]
|
||||
len_ok = len(alias_hexhash) == RNS.Identity.TRUNCATED_HASHLENGTH//8*2
|
||||
try: alias_hash = bytes.fromhex(alias_hexhash)
|
||||
except: alias_hash = None
|
||||
alias_exists = alias in self.destination_aliases
|
||||
if not len_ok or not alias_hash: continue
|
||||
if alias_exists: continue
|
||||
self.destination_aliases[alias] = RNS.hexrep(alias_hash, delimit=False)
|
||||
|
||||
if not os.path.isfile(self.identitypath):
|
||||
identity = RNS.Identity()
|
||||
identity.to_file(self.identitypath)
|
||||
@@ -185,6 +198,19 @@ class ReticulumGitClient():
|
||||
|
||||
else: self.identity = identity
|
||||
|
||||
self.destination_hexhash = self.__resolve_destination_alias(self.destination_hexhash)
|
||||
|
||||
def __resolve_destination_alias(self, alias):
|
||||
def resolve(alias):
|
||||
len_match = len(alias) == RNS.Identity.TRUNCATED_HASHLENGTH//8*2
|
||||
try: hash_bytes = bytes.fromhex(alias)
|
||||
except: hash_bytes = None
|
||||
if len_match and hash_bytes: return alias
|
||||
else: return self.destination_aliases[alias] if alias in self.destination_aliases else alias
|
||||
|
||||
resolved = resolve(alias)
|
||||
return resolved
|
||||
|
||||
def abort(self, reason=None, code=255):
|
||||
if not reason: reason = "Unknown reason"
|
||||
print(f"git-remote-rns failed: {reason}", file=sys.stderr)
|
||||
@@ -656,6 +682,21 @@ __default_rngit_config__ = '''# This is the default rngit client config file.
|
||||
|
||||
ref_batch_size = 25
|
||||
|
||||
|
||||
[aliases]
|
||||
|
||||
# You can define aliases for commonly used destination
|
||||
# hashes in this section. Each line must be in the format
|
||||
# aliased_name = DESTINATION_HASH
|
||||
#
|
||||
# These hashes are used for resolving remote destinations.
|
||||
# For rngit node permissions and identity resolution,
|
||||
# aliases must be defined in ~/.rngit/config.
|
||||
|
||||
# my_node = 063d38912bffc850af4a1b8a270a9d85
|
||||
# bobs_node = 714981d03e41deda0e4468cb274414cc
|
||||
|
||||
|
||||
[logging]
|
||||
# Valid log levels are 0 through 7:
|
||||
# 0: Log only critical information
|
||||
|
||||
@@ -437,6 +437,10 @@ class NomadNetworkNode():
|
||||
|
||||
repo = self.get_accessible_repository(remote_identity, group_name, repo_name)
|
||||
|
||||
if not repo:
|
||||
content = self.m_heading("Not Found", 1) + "\nThe requested repository was not found.\n"
|
||||
return self.render_template(content, nav_content="".join(nav_parts), st=st)
|
||||
|
||||
repo_source = ""; source_link = None
|
||||
if repo["fork"] or repo["mirror"]:
|
||||
if repo["fork"]: source_type = "fork"; source_url = repo["fork"]
|
||||
@@ -457,15 +461,14 @@ class NomadNetworkNode():
|
||||
source_link = f"{mu_link}"
|
||||
except Exception as e: source_link = ""
|
||||
|
||||
synced_ago = max(0, time.time()-self.owner.last_upstream_sync(repo["path"]))
|
||||
sync_time = RNS.prettytime(synced_ago, compact=True).split(" ")[0]
|
||||
sync_str = f" `*{self.CLR_DIM_H}synced {sync_time} ago`f`*\n"
|
||||
source_desc = f"{source_type}ed from"
|
||||
source_indent = " "*(len(f"Node / {group_name} / {repo_name}")-len(source_desc))
|
||||
if source_link: source_url = source_link
|
||||
nav_parts.append(f"{self.CLR_DIM}{source_desc.capitalize()}{source_indent} {source_url}`f\n")
|
||||
nav_parts.append(f"{self.CLR_DIM}{source_desc.capitalize()}{source_indent} {source_url}`f{sync_str}\n")
|
||||
|
||||
if not repo:
|
||||
content = self.m_heading("Not Found", 1) + "\nThe requested repository was not found.\n"
|
||||
return self.render_template(content, nav_content="".join(nav_parts), st=st)
|
||||
|
||||
description = self.get_repository_description(repo["path"])
|
||||
if description: description = f"{description}\n\n"
|
||||
else: description = ""
|
||||
@@ -702,6 +705,7 @@ class NomadNetworkNode():
|
||||
content = self.m_heading("Invalid Path", 1) + "\n\nNo file path specified.\n"
|
||||
return self.render_template(content, st=st)
|
||||
|
||||
file_path = file_path.lstrip("./").replace("/./", "/")
|
||||
file_ext = os.path.splitext(file_path)[1].lower()
|
||||
renderable = file_ext in self.RENDERABLE_EXTS
|
||||
if not renderable: raw = True; render = False
|
||||
@@ -2571,7 +2575,7 @@ class NomadNetworkNode():
|
||||
first = str(labels[0])[:12]
|
||||
last = str(labels[-1])[:12]
|
||||
mid_space = len(bottom) - len(first) - len(last)
|
||||
lines.append(f"{self.CLR_DIM}{first}{" " * mid_space}{last}`f\n")
|
||||
lines.append(f"{self.CLR_DIM}{first}{' ' * mid_space}{last}`f\n")
|
||||
|
||||
return "".join(lines)
|
||||
|
||||
|
||||
+773
-77
File diff suppressed because it is too large
Load Diff
+70
-23
@@ -208,19 +208,21 @@ class MarkdownToMicron:
|
||||
code_content = '\n'.join(code_buffer)
|
||||
|
||||
if self.syntax_highlighter and code_block_lang:
|
||||
try:
|
||||
highlighted = self.syntax_highlighter.highlight(code_content, language=code_block_lang)
|
||||
result_lines.append(f"{self.CODE_BG}{self.CODE_FG}")
|
||||
result_lines.append(highlighted)
|
||||
result_lines.append(self.CODE_RESET)
|
||||
if code_block_lang.lower() == "rawmu": result_lines.append(code_content)
|
||||
else:
|
||||
try:
|
||||
highlighted = self.syntax_highlighter.highlight(code_content, language=code_block_lang)
|
||||
result_lines.append(f"{self.CODE_BG}{self.CODE_FG}")
|
||||
result_lines.append(highlighted)
|
||||
result_lines.append(self.CODE_RESET)
|
||||
|
||||
except Exception:
|
||||
# Fallback to plain literal block on any error
|
||||
result_lines.append(f"{self.CODE_BG}{self.CODE_FG}")
|
||||
result_lines.append(self.LITERAL_START)
|
||||
result_lines.append(self._escape_literals(code_content))
|
||||
result_lines.append(self.LITERAL_END)
|
||||
result_lines.append(self.CODE_RESET)
|
||||
except Exception:
|
||||
# Fallback to plain literal block on any error
|
||||
result_lines.append(f"{self.CODE_BG}{self.CODE_FG}")
|
||||
result_lines.append(self.LITERAL_START)
|
||||
result_lines.append(self._escape_literals(code_content))
|
||||
result_lines.append(self.LITERAL_END)
|
||||
result_lines.append(self.CODE_RESET)
|
||||
else:
|
||||
result_lines.append(f"{self.CODE_BG}{self.CODE_FG}")
|
||||
result_lines.append(self.LITERAL_START)
|
||||
@@ -684,19 +686,64 @@ class MarkdownToMicron:
|
||||
return " " * left + text + " " * right
|
||||
else:
|
||||
return text + " " * padding
|
||||
|
||||
|
||||
def _truncate_cell(self, text, width):
|
||||
if self._visible_width(text) <= width: return text
|
||||
|
||||
stripped = text
|
||||
stripped = re.sub(r'`[FB][0-9a-fA-F]{3}', '', stripped)
|
||||
stripped = re.sub(r'`[!*_]', '', stripped)
|
||||
stripped = re.sub(r'`f`b', '', stripped)
|
||||
|
||||
if len(stripped) <= width - 1: return text
|
||||
|
||||
truncated = stripped[:width - 1] + "…"
|
||||
return truncated
|
||||
|
||||
truncation_point = len(text)
|
||||
while truncation_point > 0 and self._visible_width(text[0:truncation_point]) >= width:
|
||||
truncation_point -= 1
|
||||
|
||||
truncated = text[:truncation_point]
|
||||
|
||||
# Yes, this is convoluted, but if someone else has
|
||||
# a better idea on how to handle unclosed micron
|
||||
# tags in the truncated cells, I'm all ears.
|
||||
active_tags = set()
|
||||
fg_active = False
|
||||
bg_active = False
|
||||
|
||||
i = 0
|
||||
while i < len(truncated):
|
||||
if truncated[i] == '`':
|
||||
if i + 1 < len(truncated):
|
||||
tag_char = truncated[i + 1]
|
||||
|
||||
if tag_char in '!*_=':
|
||||
if tag_char in active_tags: active_tags.remove(tag_char)
|
||||
else: active_tags.add(tag_char)
|
||||
i += 2
|
||||
continue
|
||||
|
||||
elif tag_char == 'f':
|
||||
fg_active = False
|
||||
i += 2
|
||||
continue
|
||||
|
||||
elif tag_char == 'b':
|
||||
bg_active = False
|
||||
i += 2
|
||||
continue
|
||||
|
||||
elif tag_char == 'F':
|
||||
fg_active = True
|
||||
if i + 2 < len(truncated) and truncated[i + 2] == 'T': i += 8
|
||||
else: i += 5
|
||||
continue
|
||||
|
||||
elif tag_char == 'B':
|
||||
bg_active = True
|
||||
if i + 2 < len(truncated) and truncated[i + 2] == 'T': i += 8
|
||||
else: i += 5
|
||||
continue
|
||||
i += 1
|
||||
|
||||
closers = []
|
||||
if fg_active: closers.append('`f')
|
||||
if bg_active: closers.append('`b')
|
||||
for fmt in active_tags: closers.append(f'`{fmt}')
|
||||
|
||||
return truncated + ''.join(closers) + "…"
|
||||
|
||||
def _wrap_text(self, text, width):
|
||||
if not text: return [""]
|
||||
|
||||
+92
-13
@@ -40,12 +40,15 @@ import base64
|
||||
|
||||
from RNS._version import __version__
|
||||
from RNS.vendor import umsgpack as mp
|
||||
from RNS.vendor.configobj import ConfigObj
|
||||
from RNS.vendor.validate import Validator
|
||||
from RNS.Cryptography.Hashes import sha256
|
||||
from RNS.Cryptography.Hashes import file_sha256
|
||||
|
||||
APP_NAME = "rns"
|
||||
DEFAULT_ASPECTS = f"{APP_NAME}.id"
|
||||
NO_MESSAGE = 0x01
|
||||
NO_META = 0x02
|
||||
|
||||
PRV_EXT = "rid"
|
||||
PUB_EXT = "pub"
|
||||
@@ -122,11 +125,14 @@ def main():
|
||||
parser.add_argument("-e", "--encrypt", metavar="file", action="store", nargs="*", default=None, help="encrypt file")
|
||||
parser.add_argument("-V", "--validate", metavar="path", action="store", nargs="*", default=None, help="validate signature")
|
||||
parser.add_argument("-s", "--sign", metavar="path", action="store", nargs="*", default=None, help="sign file")
|
||||
parser.add_argument("-S", "--sign-message", metavar="path", action="store", nargs="?", const=NO_MESSAGE, default=None, help="create embedded signed message")
|
||||
parser.add_argument("-S", "--sign-message", metavar="text", action="store", nargs="?", const=NO_MESSAGE, default=None, help="create embedded signed message")
|
||||
parser.add_argument("-E", "--embed-meta", metavar="path", action="store", nargs="?", const=NO_META, default=None, help="embed metadata structure from file")
|
||||
parser.add_argument("--meta-spec", metavar="path", action="store", nargs="?", default=None, help="validate metadata for embedding with spec from file")
|
||||
parser.add_argument("--raw", action="store_true", default=False, help="sign raw input data instead of hashing first")
|
||||
|
||||
# I/O Control
|
||||
parser.add_argument("-w", "--write", metavar="file", action="store", default=None, help="output file path", type=str)
|
||||
parser.add_argument("-w", "--write", metavar="path", action="store", default=None, help="output file path", type=str)
|
||||
parser.add_argument("-r", "--read", metavar="path", action="store", default=None, help="input file path for operations with optional file input", type=str)
|
||||
parser.add_argument("-f", "--force", action="store_true", default=None, help="write output even if it overwrites existing files")
|
||||
parser.add_argument("-I", "--stdin", action="store_true", default=False, help=argparse.SUPPRESS) # help="read input from STDIN instead of file"
|
||||
parser.add_argument("-O", "--stdout", action="store_true", default=False, help=argparse.SUPPRESS) # help="write output to STDOUT instead of file"
|
||||
@@ -139,17 +145,18 @@ def main():
|
||||
parser.add_argument("-P", "--print-private", action="store_true", default=False, help="allow displaying private keys")
|
||||
|
||||
# Formatting Control
|
||||
parser.add_argument("-b", "--base64", action="store_true", default=False, help="Use base64-encoded input and output")
|
||||
parser.add_argument("-B", "--base32", action="store_true", default=False, help="Use base32-encoded input and output")
|
||||
parser.add_argument("--hex", action="store_true", default=False, help="Use hex-encoded input and output")
|
||||
parser.add_argument("--base256", action="store_true", default=False, help="Use base256-encoded input and output")
|
||||
parser.add_argument("-b", "--base64", action="store_true", default=False, help="Use base64-encoded input and output")
|
||||
parser.add_argument("-U", "--base256", action="store_true", default=False, help="Use base256-encoded input and output")
|
||||
parser.add_argument("-F", "--hex", action="store_true", default=False, help="Use hex-encoded input and output")
|
||||
parser.add_argument("--meta", action="store_true", default=False, help="Display RSM metadata if available")
|
||||
|
||||
parser.add_argument("--version", action="version", version="rnid {version}".format(version=__version__))
|
||||
|
||||
args = parser.parse_args()
|
||||
validate_args(args)
|
||||
|
||||
op_requires_identity = (args.sign or args.encrypt or args.decrypt or args.announce or args.write
|
||||
op_requires_identity = (args.sign or args.sign_message or args.encrypt or args.decrypt or args.announce or args.write
|
||||
or args.print_identity or args.print_identity or args.export_pub or args.export_prv)
|
||||
|
||||
identity = get_operating_identity(args, allow_none=not op_requires_identity, no_cache=args.no_cache); op = False
|
||||
@@ -456,7 +463,6 @@ def validate_rsg(rsg, message=None, required_signer=None):
|
||||
if not "meta" in signed_data: return False, None, None
|
||||
if not "signer" in signed_data["meta"]: return False, None, None
|
||||
if not "pubkey" in signed_data["meta"]: return False, None, None
|
||||
if not "note" in signed_data["meta"]: return False, None, None
|
||||
|
||||
try:
|
||||
if type(required_signer) == RNS.Identity:
|
||||
@@ -479,7 +485,7 @@ def validate_rsg(rsg, message=None, required_signer=None):
|
||||
|
||||
return False, signed_data, signing_identity
|
||||
|
||||
def create_rsg(signer_identity, message, embed=False, note=None, meta=None, output="bin"):
|
||||
def create_rsg(signer_identity, message, embed=False, meta=None, output="bin"):
|
||||
if not output in ["bin", "hex", "base32", "base256", "base64"]: raise TypeError(f"Invalid output format for rsg creation")
|
||||
if not type(signer_identity) == RNS.Identity: raise TypeError(f"{signer_identity} is not a Reticulum Identity")
|
||||
if not signer_identity.get_private_key(): raise ValueError(f"{signer_identity} does not hold a private key")
|
||||
@@ -487,7 +493,7 @@ def create_rsg(signer_identity, message, embed=False, note=None, meta=None, outp
|
||||
signed_data = { "hashtype": "sha256", "hash": get_rsg_hash(message),
|
||||
"meta": { "signer": signer_identity.hash,
|
||||
"pubkey": signer_identity.get_public_key(),
|
||||
"note" : note } }
|
||||
"note" : None } } # TODO: Remove default note field in 1.2.9
|
||||
|
||||
if embed:
|
||||
if type(message) == str: message = message.encode("utf-8")
|
||||
@@ -495,7 +501,7 @@ def create_rsg(signer_identity, message, embed=False, note=None, meta=None, outp
|
||||
|
||||
if meta and type(meta) == dict:
|
||||
for key in meta:
|
||||
if not key in signed_data["meta"]: signed_data["meta"]["key"] = meta["key"]
|
||||
if not key in signed_data["meta"]: signed_data["meta"][key] = meta[key]
|
||||
|
||||
envelope = mp.packb(signed_data)
|
||||
signature = signer_identity.sign(envelope)
|
||||
@@ -558,6 +564,27 @@ def unwrap_rsg(wrapped_rsg):
|
||||
|
||||
return unwrapped if unwrapped else None
|
||||
|
||||
def rsg_meta_from_file(path, spec_path=None):
|
||||
if spec_path: meta_spec = ConfigObj(spec_path)
|
||||
else: meta_spec = None
|
||||
parsed = ConfigObj(path, configspec=meta_spec)
|
||||
|
||||
if meta_spec:
|
||||
validation = parsed.validate(Validator())
|
||||
if not validation == True: raise ValueError("Metadata did not pass spec validation")
|
||||
|
||||
return parsed.dict()
|
||||
|
||||
def rsg_meta_from_str(meta, spec=None):
|
||||
if spec: meta_spec = ConfigObj(spec.splitlines())
|
||||
else: meta_spec = None
|
||||
parsed = ConfigObj(meta.splitlines(), configspec=meta_spec)
|
||||
|
||||
if meta_spec:
|
||||
validation = parsed.validate(Validator())
|
||||
if not validation == True: raise ValueError("Metadata did not pass spec validation")
|
||||
|
||||
return parsed.dict()
|
||||
|
||||
###################################
|
||||
# Signing & Validation Operations #
|
||||
@@ -656,7 +683,40 @@ def validate_message(args, identity, __recursive=False):
|
||||
signer_description = f"\nThe message was NOT signed by {identity_str or signing_identity}" if identity else ""
|
||||
if not valid: print(f"Invalid signature in {signature_path}{signer_description}"); exit(R_INVALID_SIGNATURE)
|
||||
else:
|
||||
print(f"\nSignature is valid, the following message was signed by {signing_identity}:\n")
|
||||
if args.meta:
|
||||
print("RSM Metadata\n============\n")
|
||||
def recurse(entry, key, level=1):
|
||||
try:
|
||||
indent = " "*level
|
||||
if type(entry) == dict:
|
||||
print(f"d{indent}{key}:")
|
||||
for key in entry: recurse(entry[key], key, level=level+1)
|
||||
else:
|
||||
maxwidth = 64
|
||||
etype = "u"
|
||||
if type(entry) == str: etype = "s"
|
||||
elif type(entry) == bytes: etype = "b"
|
||||
elif type(entry) == list: etype = "l"
|
||||
elif type(entry) == dict: etype = "d"
|
||||
elif type(entry) == int: etype = "i"
|
||||
elif type(entry) == float: etype = "f"
|
||||
elif entry == None: etype = "N"
|
||||
if key == "note" and entry == None: return # TODO: Remove this check in 1.2.9
|
||||
if type(entry) == bytes: entry = RNS.hexrep(entry, delimit=False)
|
||||
leadin = f"{etype}{indent}{key}="; leadln = len(leadin)
|
||||
entry = f"{entry}"; chunk = entry[:maxwidth]; entry = entry[maxwidth:]
|
||||
print(f"{leadin}{chunk}")
|
||||
while len(entry): chunk = entry[:maxwidth]; entry = entry[maxwidth:]; print(f" "*leadln+chunk)
|
||||
except: print(f"E{indent}{key}=<Decode Error>")
|
||||
|
||||
meta = signed_data["meta"]
|
||||
for key in meta: entry = meta[key]; recurse(entry, key)
|
||||
print("\nValidation\n==========")
|
||||
|
||||
c = ":" if not args.meta else ""
|
||||
f = " following" if not args.meta else ""
|
||||
print(f"\nSignature is valid, the{f} message was signed by {signing_identity}{c}\n")
|
||||
if args.meta: print("Message\n=======\n")
|
||||
print(signed_data["message"].decode("utf-8"))
|
||||
|
||||
return exit(R_OK) if not __recursive else R_OK
|
||||
@@ -714,6 +774,7 @@ def sign(args, identity, __recursive=False):
|
||||
|
||||
def sign_message(args, identity):
|
||||
message = args.sign_message
|
||||
meta = None
|
||||
|
||||
if args.base32: output = "base32"
|
||||
elif args.base64: output = "base64"
|
||||
@@ -722,13 +783,31 @@ def sign_message(args, identity):
|
||||
else: output = "bin"
|
||||
|
||||
if output == "bin" and not args.write: print("No write path specified"); exit(R_INVALID_ARGS)
|
||||
if not identity.get_private_key(): print(f"Cannot sign \"{sign_path}\", the identity does not hold a private key"); exit(R_NO_PRVKEY)
|
||||
if not identity: print(f"Cannot sign, no working identity available"); exit(R_NO_IDENTITY)
|
||||
if not identity.get_private_key(): print(f"Cannot sign, the identity does not hold a private key"); exit(R_NO_PRVKEY)
|
||||
|
||||
if args.read:
|
||||
if message != NO_MESSAGE: print("Both an input file and command-line provided message was specified, aborting"); exit(R_INVALID_ARGS)
|
||||
sign_path = os.path.expanduser(args.read)
|
||||
if not os.path.isfile(sign_path): print(f"The file {sign_path} does not exist"); exit(R_NO_FILE)
|
||||
with open(sign_path, "r", encoding="utf-8") as fh: message = fh.read()
|
||||
|
||||
if message == NO_MESSAGE: message = get_editor_content()
|
||||
if not message: print("No message specified"); exit(R_INVALID_ARGS)
|
||||
|
||||
if args.embed_meta:
|
||||
meta_path = os.path.expanduser(args.embed_meta)
|
||||
meta_spec_path = meta_path+".spec" if not args.meta_spec else args.meta_spec
|
||||
if not os.path.isfile(meta_path): print(f"Metadata file {meta_path} does not exist"); exit(R_NO_FILE)
|
||||
if not os.path.isfile(meta_spec_path): meta_spec_path = None
|
||||
spec_info = f" using spec from {meta_spec_path}" if meta_spec_path else ""
|
||||
print(f"Embedding metadata from {meta_path}{spec_info}")
|
||||
|
||||
try: meta = rsg_meta_from_file(meta_path, spec_path=meta_spec_path)
|
||||
except Exception as e: print(f"Could not load metadata from {meta_path}: {e}"); exit(R_UNKNOWN_ERROR)
|
||||
|
||||
try:
|
||||
rsg = create_rsg(identity, message, embed=True, output=output)
|
||||
rsg = create_rsg(identity, message, embed=True, meta=meta, output=output)
|
||||
if not rsg: print(f"No signature created, not writing"); exit(R_UNKNOWN_ERROR)
|
||||
|
||||
if output == "bin":
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__ = "1.2.7"
|
||||
__version__ = "1.2.8"
|
||||
|
||||
Vendored
+537
@@ -0,0 +1,537 @@
|
||||
# validate.py
|
||||
# -*- coding: utf-8 -*-
|
||||
# pylint: disable=
|
||||
#
|
||||
# A Validator object.
|
||||
#
|
||||
# Copyright (C) 2005-2014:
|
||||
# (name) : (email)
|
||||
# Michael Foord: fuzzyman AT voidspace DOT org DOT uk
|
||||
# Mark Andrews: mark AT la-la DOT com
|
||||
# Nicola Larosa: nico AT tekNico DOT net
|
||||
# Rob Dennis: rdennis AT gmail DOT com
|
||||
# Eli Courtwright: eli AT courtwright DOT org
|
||||
|
||||
# This software is licensed under the terms of the BSD license.
|
||||
# http://opensource.org/licenses/BSD-3-Clause
|
||||
|
||||
# ConfigObj 5 - main repository for documentation and issue tracking:
|
||||
# https://github.com/DiffSK/configobj
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pprint import pprint
|
||||
|
||||
__version__ = '1.0.1'
|
||||
|
||||
__all__ = (
|
||||
'dottedQuadToNum',
|
||||
'numToDottedQuad',
|
||||
'ValidateError',
|
||||
'VdtUnknownCheckError',
|
||||
'VdtParamError',
|
||||
'VdtTypeError',
|
||||
'VdtValueError',
|
||||
'VdtValueTooSmallError',
|
||||
'VdtValueTooBigError',
|
||||
'VdtValueTooShortError',
|
||||
'VdtValueTooLongError',
|
||||
'VdtMissingValue',
|
||||
'Validator',
|
||||
'is_integer',
|
||||
'is_float',
|
||||
'is_boolean',
|
||||
'is_list',
|
||||
'is_tuple',
|
||||
'is_ip_addr',
|
||||
'is_string',
|
||||
'is_int_list',
|
||||
'is_bool_list',
|
||||
'is_float_list',
|
||||
'is_string_list',
|
||||
'is_ip_addr_list',
|
||||
'is_mixed_list',
|
||||
'is_option',
|
||||
)
|
||||
|
||||
_list_arg = re.compile(r'''
|
||||
(?:
|
||||
([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*list\(
|
||||
(
|
||||
(?:
|
||||
\s*
|
||||
(?:
|
||||
(?:".*?")| # double quotes
|
||||
(?:'.*?')| # single quotes
|
||||
(?:[^'",\s\)][^,\)]*?) # unquoted
|
||||
)
|
||||
\s*,\s*
|
||||
)*
|
||||
(?:
|
||||
(?:".*?")| # double quotes
|
||||
(?:'.*?')| # single quotes
|
||||
(?:[^'",\s\)][^,\)]*?) # unquoted
|
||||
)? # last one
|
||||
)
|
||||
\)
|
||||
)
|
||||
''', re.VERBOSE | re.DOTALL) # two groups
|
||||
|
||||
_list_members = re.compile(r'''
|
||||
(
|
||||
(?:".*?")| # double quotes
|
||||
(?:'.*?')| # single quotes
|
||||
(?:[^'",\s=][^,=]*?) # unquoted
|
||||
)
|
||||
(?:
|
||||
(?:\s*,\s*)|(?:\s*$) # comma
|
||||
)
|
||||
''', re.VERBOSE | re.DOTALL) # one group
|
||||
|
||||
_paramstring = r'''
|
||||
(?:
|
||||
(
|
||||
(?:
|
||||
[a-zA-Z_][a-zA-Z0-9_]*\s*=\s*list\(
|
||||
(?:
|
||||
\s*
|
||||
(?:
|
||||
(?:".*?")| # double quotes
|
||||
(?:'.*?')| # single quotes
|
||||
(?:[^'",\s\)][^,\)]*?) # unquoted
|
||||
)
|
||||
\s*,\s*
|
||||
)*
|
||||
(?:
|
||||
(?:".*?")| # double quotes
|
||||
(?:'.*?')| # single quotes
|
||||
(?:[^'",\s\)][^,\)]*?) # unquoted
|
||||
)? # last one
|
||||
\)
|
||||
)|
|
||||
(?:
|
||||
(?:".*?")| # double quotes
|
||||
(?:'.*?')| # single quotes
|
||||
(?:[^'",\s=][^,=]*?)| # unquoted
|
||||
(?: # keyword argument
|
||||
[a-zA-Z_][a-zA-Z0-9_]*\s*=\s*
|
||||
(?:
|
||||
(?:".*?")| # double quotes
|
||||
(?:'.*?')| # single quotes
|
||||
(?:[^'",\s=][^,=]*?) # unquoted
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(?:
|
||||
(?:\s*,\s*)|(?:\s*$) # comma
|
||||
)
|
||||
)
|
||||
'''
|
||||
|
||||
_matchstring = '^%s*' % _paramstring
|
||||
|
||||
def dottedQuadToNum(ip):
|
||||
# import here to avoid it when ip_addr values are not used
|
||||
import socket, struct
|
||||
|
||||
try:
|
||||
return struct.unpack('!L',
|
||||
socket.inet_aton(ip.strip()))[0]
|
||||
except socket.error:
|
||||
raise ValueError('Not a good dotted-quad IP: %s' % ip)
|
||||
return
|
||||
|
||||
def numToDottedQuad(num):
|
||||
# import here to avoid it when ip_addr values are not used
|
||||
import socket, struct
|
||||
|
||||
# no need to intercept here, 4294967295L is fine
|
||||
if num > int(4294967295) or num < 0:
|
||||
raise ValueError('Not a good numeric IP: %s' % num)
|
||||
try:
|
||||
return socket.inet_ntoa(
|
||||
struct.pack('!L', int(num)))
|
||||
except (socket.error, struct.error, OverflowError):
|
||||
raise ValueError('Not a good numeric IP: %s' % num)
|
||||
|
||||
class ValidateError(Exception):
|
||||
"""
|
||||
This error indicates that the check failed.
|
||||
It can be the base class for more specific errors.
|
||||
"""
|
||||
|
||||
class VdtMissingValue(ValidateError):
|
||||
"""No value was supplied to a check that needed one."""
|
||||
|
||||
class VdtUnknownCheckError(ValidateError):
|
||||
def __init__(self, value):
|
||||
ValidateError.__init__(self, 'the check "{}" is unknown.'.format(value))
|
||||
|
||||
|
||||
class VdtParamError(SyntaxError):
|
||||
NOT_GIVEN = object()
|
||||
|
||||
def __init__(self, name_or_msg, value=NOT_GIVEN):
|
||||
if value is self.NOT_GIVEN:
|
||||
SyntaxError.__init__(self, name_or_msg)
|
||||
else:
|
||||
SyntaxError.__init__(self, 'passed an incorrect value "{}" for parameter "{}".'.format(value, name_or_msg))
|
||||
|
||||
|
||||
class VdtTypeError(ValidateError):
|
||||
def __init__(self, value):
|
||||
ValidateError.__init__(self, 'the value "{}" is of the wrong type.'.format(value))
|
||||
|
||||
|
||||
class VdtValueError(ValidateError):
|
||||
def __init__(self, value):
|
||||
ValidateError.__init__(self, 'the value "{}" is unacceptable.'.format(value))
|
||||
|
||||
|
||||
class VdtValueTooSmallError(VdtValueError):
|
||||
def __init__(self, value):
|
||||
ValidateError.__init__(self, 'the value "{}" is too small.'.format(value))
|
||||
|
||||
|
||||
class VdtValueTooBigError(VdtValueError):
|
||||
def __init__(self, value):
|
||||
ValidateError.__init__(self, 'the value "{}" is too big.'.format(value))
|
||||
|
||||
|
||||
class VdtValueTooShortError(VdtValueError):
|
||||
def __init__(self, value):
|
||||
ValidateError.__init__(
|
||||
self,
|
||||
'the value "{}" is too short.'.format(value))
|
||||
|
||||
class VdtValueTooLongError(VdtValueError):
|
||||
def __init__(self, value):
|
||||
ValidateError.__init__(self, 'the value "{}" is too long.'.format(value))
|
||||
|
||||
class Validator(object):
|
||||
# this regex does the initial parsing of the checks
|
||||
_func_re = re.compile(r'([^\(\)]+?)\((.*)\)', re.DOTALL)
|
||||
|
||||
# this regex takes apart keyword arguments
|
||||
_key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$', re.DOTALL)
|
||||
|
||||
|
||||
# this regex finds keyword=list(....) type values
|
||||
_list_arg = _list_arg
|
||||
|
||||
# this regex takes individual values out of lists - in one pass
|
||||
_list_members = _list_members
|
||||
|
||||
# These regexes check a set of arguments for validity
|
||||
# and then pull the members out
|
||||
_paramfinder = re.compile(_paramstring, re.VERBOSE | re.DOTALL)
|
||||
_matchfinder = re.compile(_matchstring, re.VERBOSE | re.DOTALL)
|
||||
|
||||
def __init__(self, functions=None):
|
||||
self.functions = {
|
||||
'': self._pass,
|
||||
'integer': is_integer,
|
||||
'float': is_float,
|
||||
'boolean': is_boolean,
|
||||
'ip_addr': is_ip_addr,
|
||||
'string': is_string,
|
||||
'list': is_list,
|
||||
'tuple': is_tuple,
|
||||
'int_list': is_int_list,
|
||||
'float_list': is_float_list,
|
||||
'bool_list': is_bool_list,
|
||||
'ip_addr_list': is_ip_addr_list,
|
||||
'string_list': is_string_list,
|
||||
'mixed_list': is_mixed_list,
|
||||
'pass': self._pass,
|
||||
'option': is_option,
|
||||
'force_list': force_list,
|
||||
}
|
||||
if functions is not None:
|
||||
self.functions.update(functions)
|
||||
# tekNico: for use by ConfigObj
|
||||
self.baseErrorClass = ValidateError
|
||||
self._cache = {}
|
||||
|
||||
def check(self, check, value, missing=False):
|
||||
fun_name, fun_args, fun_kwargs, default = self._parse_with_caching(check)
|
||||
|
||||
if missing:
|
||||
if default is None:
|
||||
# no information needed here - to be handled by caller
|
||||
raise VdtMissingValue()
|
||||
value = self._handle_none(default)
|
||||
|
||||
if value is None:
|
||||
return None
|
||||
|
||||
return self._check_value(value, fun_name, fun_args, fun_kwargs)
|
||||
|
||||
def _handle_none(self, value):
|
||||
if value == 'None':
|
||||
return None
|
||||
elif value in ("'None'", '"None"'):
|
||||
# Special case a quoted None
|
||||
value = self._unquote(value)
|
||||
return value
|
||||
|
||||
def _parse_with_caching(self, check):
|
||||
if check in self._cache:
|
||||
fun_name, fun_args, fun_kwargs, default = self._cache[check]
|
||||
# We call list and dict below to work with *copies* of the data
|
||||
# rather than the original (which are mutable of course)
|
||||
fun_args = list(fun_args)
|
||||
fun_kwargs = dict(fun_kwargs)
|
||||
else:
|
||||
fun_name, fun_args, fun_kwargs, default = self._parse_check(check)
|
||||
fun_kwargs = {str(key): value for (key, value) in list(fun_kwargs.items())}
|
||||
self._cache[check] = fun_name, list(fun_args), dict(fun_kwargs), default
|
||||
return fun_name, fun_args, fun_kwargs, default
|
||||
|
||||
def _check_value(self, value, fun_name, fun_args, fun_kwargs):
|
||||
try:
|
||||
fun = self.functions[fun_name]
|
||||
except KeyError:
|
||||
raise VdtUnknownCheckError(fun_name)
|
||||
else:
|
||||
return fun(value, *fun_args, **fun_kwargs)
|
||||
|
||||
def _parse_check(self, check):
|
||||
fun_match = self._func_re.match(check)
|
||||
if fun_match:
|
||||
fun_name = fun_match.group(1)
|
||||
arg_string = fun_match.group(2)
|
||||
arg_match = self._matchfinder.match(arg_string)
|
||||
if arg_match is None:
|
||||
# Bad syntax
|
||||
raise VdtParamError('Bad syntax in check "%s".' % check)
|
||||
fun_args = []
|
||||
fun_kwargs = {}
|
||||
# pull out args of group 2
|
||||
for arg in self._paramfinder.findall(arg_string):
|
||||
# args may need whitespace removing (before removing quotes)
|
||||
arg = arg.strip()
|
||||
listmatch = self._list_arg.match(arg)
|
||||
if listmatch:
|
||||
key, val = self._list_handle(listmatch)
|
||||
fun_kwargs[key] = val
|
||||
continue
|
||||
keymatch = self._key_arg.match(arg)
|
||||
if keymatch:
|
||||
val = keymatch.group(2)
|
||||
if not val in ("'None'", '"None"'):
|
||||
# Special case a quoted None
|
||||
val = self._unquote(val)
|
||||
fun_kwargs[keymatch.group(1)] = val
|
||||
continue
|
||||
|
||||
fun_args.append(self._unquote(arg))
|
||||
else:
|
||||
# allows for function names without (args)
|
||||
return check, (), {}, None
|
||||
|
||||
# Default must be deleted if the value is specified too,
|
||||
# otherwise the check function will get a spurious "default" keyword arg
|
||||
default = fun_kwargs.pop('default', None)
|
||||
return fun_name, fun_args, fun_kwargs, default
|
||||
|
||||
def _unquote(self, val):
|
||||
if (len(val) >= 2) and (val[0] in ("'", '"')) and (val[0] == val[-1]):
|
||||
val = val[1:-1]
|
||||
return val
|
||||
|
||||
def _list_handle(self, listmatch):
|
||||
out = []
|
||||
name = listmatch.group(1)
|
||||
args = listmatch.group(2)
|
||||
for arg in self._list_members.findall(args):
|
||||
out.append(self._unquote(arg))
|
||||
return name, out
|
||||
|
||||
def _pass(self, value):
|
||||
return value
|
||||
|
||||
def get_default_value(self, check):
|
||||
fun_name, fun_args, fun_kwargs, default = self._parse_with_caching(check)
|
||||
if default is None:
|
||||
raise KeyError('Check "%s" has no default value.' % check)
|
||||
value = self._handle_none(default)
|
||||
if value is None:
|
||||
return value
|
||||
return self._check_value(value, fun_name, fun_args, fun_kwargs)
|
||||
|
||||
def _is_num_param(names, values, to_float=False):
|
||||
fun = to_float and float or int
|
||||
out_params = []
|
||||
for (name, val) in zip(names, values):
|
||||
if val is None:
|
||||
out_params.append(val)
|
||||
elif isinstance(val, (int, float, str)):
|
||||
try:
|
||||
out_params.append(fun(val))
|
||||
except ValueError:
|
||||
raise VdtParamError(name, val)
|
||||
else:
|
||||
raise VdtParamError(name, val)
|
||||
return out_params
|
||||
|
||||
# built in checks
|
||||
# you can override these by setting the appropriate name
|
||||
# in Validator.functions
|
||||
# note: if the params are specified wrongly in your input string,
|
||||
# you will also raise errors.
|
||||
def is_integer(value, min=None, max=None):
|
||||
(min_val, max_val) = _is_num_param( # pylint: disable=unbalanced-tuple-unpacking
|
||||
('min', 'max'), (min, max))
|
||||
if not isinstance(value, (int, str)):
|
||||
raise VdtTypeError(value)
|
||||
if isinstance(value, str):
|
||||
# if it's a string - does it represent an integer ?
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
raise VdtTypeError(value)
|
||||
if (min_val is not None) and (value < min_val):
|
||||
raise VdtValueTooSmallError(value)
|
||||
if (max_val is not None) and (value > max_val):
|
||||
raise VdtValueTooBigError(value)
|
||||
return value
|
||||
|
||||
|
||||
def is_float(value, min=None, max=None):
|
||||
(min_val, max_val) = _is_num_param(
|
||||
('min', 'max'), (min, max), to_float=True)
|
||||
if not isinstance(value, (int, float, str)):
|
||||
raise VdtTypeError(value)
|
||||
if not isinstance(value, float):
|
||||
# if it's a string - does it represent a float ?
|
||||
try:
|
||||
value = float(value)
|
||||
except ValueError:
|
||||
raise VdtTypeError(value)
|
||||
if (min_val is not None) and (value < min_val):
|
||||
raise VdtValueTooSmallError(value)
|
||||
if (max_val is not None) and (value > max_val):
|
||||
raise VdtValueTooBigError(value)
|
||||
return value
|
||||
|
||||
bool_dict = {
|
||||
True: True, 'on': True, '1': True, 'true': True, 'yes': True,
|
||||
False: False, 'off': False, '0': False, 'false': False, 'no': False,
|
||||
}
|
||||
|
||||
def is_boolean(value):
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
return bool_dict[value.lower()]
|
||||
except KeyError:
|
||||
raise VdtTypeError(value)
|
||||
# we do an equality test rather than an identity test
|
||||
# this ensures Python 2.2 compatibility
|
||||
# and allows 0 and 1 to represent True and False
|
||||
if value == False:
|
||||
return False
|
||||
elif value == True:
|
||||
return True
|
||||
else:
|
||||
raise VdtTypeError(value)
|
||||
|
||||
|
||||
def is_ip_addr(value):
|
||||
if not isinstance(value, str):
|
||||
raise VdtTypeError(value)
|
||||
value = value.strip()
|
||||
try:
|
||||
dottedQuadToNum(value)
|
||||
except ValueError:
|
||||
raise VdtValueError(value)
|
||||
return value
|
||||
|
||||
|
||||
def is_list(value, min=None, max=None):
|
||||
(min_len, max_len) = _is_num_param( # pylint: disable=unbalanced-tuple-unpacking
|
||||
('min', 'max'), (min, max))
|
||||
if isinstance(value, str):
|
||||
raise VdtTypeError(value)
|
||||
try:
|
||||
num_members = len(value)
|
||||
except TypeError:
|
||||
raise VdtTypeError(value)
|
||||
if min_len is not None and num_members < min_len:
|
||||
raise VdtValueTooShortError(value)
|
||||
if max_len is not None and num_members > max_len:
|
||||
raise VdtValueTooLongError(value)
|
||||
return list(value)
|
||||
|
||||
|
||||
def is_tuple(value, min=None, max=None):
|
||||
return tuple(is_list(value, min, max))
|
||||
|
||||
def is_string(value, min=None, max=None):
|
||||
if not isinstance(value, str):
|
||||
raise VdtTypeError(value)
|
||||
(min_len, max_len) = _is_num_param(
|
||||
('min', 'max'), (min, max))
|
||||
try:
|
||||
num_members = len(value)
|
||||
except TypeError:
|
||||
raise VdtTypeError(value)
|
||||
if min_len is not None and num_members < min_len:
|
||||
raise VdtValueTooShortError(value)
|
||||
if max_len is not None and num_members > max_len:
|
||||
raise VdtValueTooLongError(value)
|
||||
return value
|
||||
|
||||
|
||||
def is_int_list(value, min=None, max=None):
|
||||
return [is_integer(mem) for mem in is_list(value, min, max)]
|
||||
|
||||
def is_bool_list(value, min=None, max=None):
|
||||
return [is_boolean(mem) for mem in is_list(value, min, max)]
|
||||
|
||||
def is_float_list(value, min=None, max=None):
|
||||
return [is_float(mem) for mem in is_list(value, min, max)]
|
||||
|
||||
def is_string_list(value, min=None, max=None):
|
||||
if isinstance(value, str):
|
||||
raise VdtTypeError(value)
|
||||
return [is_string(mem) for mem in is_list(value, min, max)]
|
||||
|
||||
def is_ip_addr_list(value, min=None, max=None):
|
||||
return [is_ip_addr(mem) for mem in is_list(value, min, max)]
|
||||
|
||||
def force_list(value, min=None, max=None):
|
||||
if not isinstance(value, (list, tuple)):
|
||||
value = [value]
|
||||
return is_list(value, min, max)
|
||||
|
||||
fun_dict = {
|
||||
int: is_integer,
|
||||
'int': is_integer,
|
||||
'integer': is_integer,
|
||||
float: is_float,
|
||||
'float': is_float,
|
||||
'ip_addr': is_ip_addr,
|
||||
str: is_string,
|
||||
'str': is_string,
|
||||
'string': is_string,
|
||||
bool: is_boolean,
|
||||
'bool': is_boolean,
|
||||
'boolean': is_boolean,
|
||||
}
|
||||
|
||||
def is_mixed_list(value, *args):
|
||||
try: length = len(value)
|
||||
except TypeError: raise VdtTypeError(value)
|
||||
if length < len(args): raise VdtValueTooShortError(value)
|
||||
elif length > len(args): raise VdtValueTooLongError(value)
|
||||
try: return [fun_dict[arg](val) for arg, val in zip(args, value)]
|
||||
except KeyError as cause: raise VdtParamError('mixed_list', cause)
|
||||
|
||||
|
||||
def is_option(value, *options):
|
||||
if not isinstance(value, str): raise VdtTypeError(value)
|
||||
if not value in options: raise VdtValueError(value)
|
||||
return value
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -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: eebf90727067dce1facd726f357a2aab
|
||||
config: d77ea3044971177c0a1e0c192353c10a
|
||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||
|
||||
@@ -348,6 +348,8 @@ Access permissions can be configured at the group level in the config file or pe
|
||||
|
||||
By default, **no** permissions are granted for anything! You will have to enable the permissions you require to be able to actually *do* something with ``rngit``.
|
||||
|
||||
Permissions can be modified by editing the ``rngit`` config file, individual ``.allowed`` files on disk, or remotely using the ``rngit perms`` command.
|
||||
|
||||
Permission Types
|
||||
----------------
|
||||
|
||||
@@ -509,6 +511,21 @@ Permission Configuration Locations
|
||||
- Repository permissions: ``<group_root>/<group_name>/<repo_name>.allowed``
|
||||
- Document permissions: ``<group_root>/<group_name>.work/<doc_id>.allowed``
|
||||
|
||||
Identity & Destination Aliases
|
||||
==============================
|
||||
|
||||
To make permission and remote destination management easier, you can locally define aliases for commonly used identity and destination hashes. Identity aliases used in permissions resolution can be defined in the ``[aliases]`` section of the ``~/.rngit/config`` file, while destination aliases are defined in the ``[aliases]`` section of the ``~/.rngit/client_config`` file.
|
||||
|
||||
All alias definitions take the form of ``aliased_name = HASH``:
|
||||
|
||||
.. code:: text
|
||||
|
||||
[aliases]
|
||||
alice = d09285e660cfe27cee6d9a0beb58b7e0
|
||||
bob = ffcffb4e255e156e77f79b82c13086a6
|
||||
|
||||
**Aliases are always resolved locally!** If for example you fork a repository with ``rngit fork rns://bobs_node/public/repo_name rns://my_node/forks/repo_name``, the forked repository will of course still reference the full, original destination hash, and use this for subsequent upstream syncs.
|
||||
|
||||
Serving Pages Over Nomad Network
|
||||
================================
|
||||
|
||||
@@ -578,6 +595,8 @@ Code blocks in Markdown can include language hints for syntax highlighting:
|
||||
print("Hello, Reticulum!")
|
||||
```
|
||||
|
||||
You can use ``rawmu`` code blocks to render raw Micron inside Markdown files. If you create a code block with the language hint ``rawmu``, everything inside it will be treated as Micron directly.
|
||||
|
||||
Customizing Templates
|
||||
---------------------
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const DOCUMENTATION_OPTIONS = {
|
||||
VERSION: '1.2.7',
|
||||
VERSION: '1.2.8',
|
||||
LANGUAGE: 'en',
|
||||
COLLAPSE_INDEX: false,
|
||||
BUILDER: 'html',
|
||||
|
||||
@@ -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.7 documentation</title>
|
||||
<title>Code Examples - Reticulum Network Stack 1.2.8 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.7 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.8 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.7 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.8 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">
|
||||
@@ -3664,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=a5753347"></script>
|
||||
</div><script src="_static/documentation_options.js?v=4d6f9085"></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>
|
||||
|
||||
@@ -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.7 documentation</title>
|
||||
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 1.2.8 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.7 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.8 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.7 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.8 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">
|
||||
@@ -295,7 +295,7 @@
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=a5753347"></script>
|
||||
</div><script src="_static/documentation_options.js?v=4d6f9085"></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,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.7 documentation</title>
|
||||
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25.dev1 --><title>Index - Reticulum Network Stack 1.2.8 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.7 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.8 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.7 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.8 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">
|
||||
@@ -839,7 +839,7 @@
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=a5753347"></script>
|
||||
</div><script src="_static/documentation_options.js?v=4d6f9085"></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>
|
||||
|
||||
@@ -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.7 documentation</title>
|
||||
<title>Getting Started Fast - Reticulum Network Stack 1.2.8 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.7 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.8 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.7 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.8 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">
|
||||
@@ -967,7 +967,7 @@ All other available modules will still be loaded when needed.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=a5753347"></script>
|
||||
</div><script src="_static/documentation_options.js?v=4d6f9085"></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>
|
||||
|
||||
+18
-4
@@ -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>Git Over Reticulum - Reticulum Network Stack 1.2.7 documentation</title>
|
||||
<title>Git Over Reticulum - Reticulum Network Stack 1.2.8 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.7 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.8 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.7 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.8 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">
|
||||
@@ -539,6 +539,7 @@ $ git config --get repository.rngit.upstream.sync
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">rngit</span></code> permission system provides fine-grained access control at multiple levels: group-level, repository-level, and document-level. Permissions can be statically configured in files or dynamically generated via executable scripts.</p>
|
||||
<p>Access permissions can be configured at the group level in the config file or per-group <code class="docutils literal notranslate"><span class="pre">.allowed</span></code> files, or per-repository <code class="docutils literal notranslate"><span class="pre">.allowed</span></code> files. 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>By default, <strong>no</strong> permissions are granted for anything! You will have to enable the permissions you require to be able to actually <em>do</em> something with <code class="docutils literal notranslate"><span class="pre">rngit</span></code>.</p>
|
||||
<p>Permissions can be modified by editing the <code class="docutils literal notranslate"><span class="pre">rngit</span></code> config file, individual <code class="docutils literal notranslate"><span class="pre">.allowed</span></code> files on disk, or remotely using the <code class="docutils literal notranslate"><span class="pre">rngit</span> <span class="pre">perms</span></code> command.</p>
|
||||
<section id="permission-types">
|
||||
<h3>Permission Types<a class="headerlink" href="#permission-types" title="Link to this heading">¶</a></h3>
|
||||
<p>The following permissions are supported:</p>
|
||||
@@ -675,6 +676,17 @@ w:none
|
||||
</ul>
|
||||
</section>
|
||||
</section>
|
||||
<section id="identity-destination-aliases">
|
||||
<h2>Identity & Destination Aliases<a class="headerlink" href="#identity-destination-aliases" title="Link to this heading">¶</a></h2>
|
||||
<p>To make permission and remote destination management easier, you can locally define aliases for commonly used identity and destination hashes. Identity aliases used in permissions resolution can be defined in the <code class="docutils literal notranslate"><span class="pre">[aliases]</span></code> section of the <code class="docutils literal notranslate"><span class="pre">~/.rngit/config</span></code> file, while destination aliases are defined in the <code class="docutils literal notranslate"><span class="pre">[aliases]</span></code> section of the <code class="docutils literal notranslate"><span class="pre">~/.rngit/client_config</span></code> file.</p>
|
||||
<p>All alias definitions take the form of <code class="docutils literal notranslate"><span class="pre">aliased_name</span> <span class="pre">=</span> <span class="pre">HASH</span></code>:</p>
|
||||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>[aliases]
|
||||
alice = d09285e660cfe27cee6d9a0beb58b7e0
|
||||
bob = ffcffb4e255e156e77f79b82c13086a6
|
||||
</pre></div>
|
||||
</div>
|
||||
<p><strong>Aliases are always resolved locally!</strong> If for example you fork a repository with <code class="docutils literal notranslate"><span class="pre">rngit</span> <span class="pre">fork</span> <span class="pre">rns://bobs_node/public/repo_name</span> <span class="pre">rns://my_node/forks/repo_name</span></code>, the forked repository will of course still reference the full, original destination hash, and use this for subsequent upstream syncs.</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 and command-line tools, <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>
|
||||
@@ -728,6 +740,7 @@ def hello_world():
|
||||
```
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You can use <code class="docutils literal notranslate"><span class="pre">rawmu</span></code> code blocks to render raw Micron inside Markdown files. If you create a code block with the language hint <code class="docutils literal notranslate"><span class="pre">rawmu</span></code>, everything inside it will be treated as Micron directly.</p>
|
||||
</section>
|
||||
<section id="customizing-templates">
|
||||
<h3>Customizing Templates<a class="headerlink" href="#customizing-templates" title="Link to this heading">¶</a></h3>
|
||||
@@ -1179,6 +1192,7 @@ options:
|
||||
<li><a class="reference internal" href="#permission-configuration-locations">Permission Configuration Locations</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#identity-destination-aliases">Identity & Destination Aliases</a></li>
|
||||
<li><a class="reference internal" href="#serving-pages-over-nomad-network">Serving Pages Over Nomad Network</a><ul>
|
||||
<li><a class="reference internal" href="#enabling-the-git-page-node">Enabling the Git Page Node</a></li>
|
||||
<li><a class="reference internal" href="#accessing-repository-pages">Accessing Repository Pages</a></li>
|
||||
@@ -1212,7 +1226,7 @@ options:
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=a5753347"></script>
|
||||
</div><script src="_static/documentation_options.js?v=4d6f9085"></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>
|
||||
|
||||
@@ -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.7 documentation</title>
|
||||
<title>Communications Hardware - Reticulum Network Stack 1.2.8 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.7 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.8 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.7 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.8 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">
|
||||
@@ -675,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=a5753347"></script>
|
||||
</div><script src="_static/documentation_options.js?v=4d6f9085"></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>
|
||||
|
||||
@@ -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.7 documentation</title>
|
||||
<title>Reticulum Network Stack 1.2.8 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.7 documentation</div></a>
|
||||
<a href="#"><div class="brand">Reticulum Network Stack 1.2.8 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.7 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.8 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">
|
||||
@@ -549,6 +549,7 @@ to participate in the development of Reticulum itself.</p>
|
||||
<li class="toctree-l3"><a class="reference internal" href="git.html#permission-configuration-locations">Permission Configuration Locations</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="git.html#identity-destination-aliases">Identity & Destination Aliases</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="git.html#serving-pages-over-nomad-network">Serving Pages Over Nomad Network</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="git.html#enabling-the-git-page-node">Enabling the Git Page Node</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="git.html#accessing-repository-pages">Accessing Repository Pages</a></li>
|
||||
@@ -683,7 +684,7 @@ to participate in the development of Reticulum itself.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=a5753347"></script>
|
||||
</div><script src="_static/documentation_options.js?v=4d6f9085"></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>
|
||||
|
||||
@@ -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.7 documentation</title>
|
||||
<title>Configuring Interfaces - Reticulum Network Stack 1.2.8 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.7 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.8 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.7 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.8 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">
|
||||
@@ -1773,7 +1773,7 @@ interface basis under the relevant interface configuration section.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=a5753347"></script>
|
||||
</div><script src="_static/documentation_options.js?v=4d6f9085"></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>
|
||||
|
||||
@@ -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.7 documentation</title>
|
||||
<title>Reticulum License - Reticulum Network Stack 1.2.8 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.7 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.8 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.7 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.8 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">
|
||||
@@ -344,7 +344,7 @@ SOFTWARE.
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=a5753347"></script>
|
||||
</div><script src="_static/documentation_options.js?v=4d6f9085"></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>
|
||||
|
||||
@@ -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>Building Networks - Reticulum Network Stack 1.2.7 documentation</title>
|
||||
<title>Building Networks - Reticulum Network Stack 1.2.8 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.7 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.8 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.7 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.8 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">
|
||||
@@ -663,7 +663,7 @@ differently than a mobile device roaming between radio cells.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=a5753347"></script>
|
||||
</div><script src="_static/documentation_options.js?v=4d6f9085"></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.
@@ -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.7 documentation</title>
|
||||
<title>API Reference - Reticulum Network Stack 1.2.8 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.7 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.8 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.7 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.8 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">
|
||||
@@ -2484,7 +2484,7 @@ will announce it.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=a5753347"></script>
|
||||
</div><script src="_static/documentation_options.js?v=4d6f9085"></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 +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.7 documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css?v=d111a655" />
|
||||
<title>Search - Reticulum Network Stack 1.2.8 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.7 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.8 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.7 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.8 documentation</span>
|
||||
|
||||
</a><form class="sidebar-search-container" method="get" action="#" role="search">
|
||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||
@@ -303,7 +303,7 @@
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=a5753347"></script>
|
||||
</div><script src="_static/documentation_options.js?v=4d6f9085"></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
@@ -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.7 documentation</title>
|
||||
<title>Programs Using Reticulum - Reticulum Network Stack 1.2.8 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.7 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.8 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.7 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.8 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">
|
||||
@@ -512,7 +512,7 @@ plugin system for expandability.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=a5753347"></script>
|
||||
</div><script src="_static/documentation_options.js?v=4d6f9085"></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>
|
||||
|
||||
@@ -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>Support Reticulum - Reticulum Network Stack 1.2.7 documentation</title>
|
||||
<title>Support Reticulum - Reticulum Network Stack 1.2.8 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.7 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.8 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.7 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.8 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">
|
||||
@@ -382,7 +382,7 @@ circumstances, so we rely on old-fashioned human feedback.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=a5753347"></script>
|
||||
</div><script src="_static/documentation_options.js?v=4d6f9085"></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>
|
||||
|
||||
@@ -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.7 documentation</title>
|
||||
<title>Understanding Reticulum - Reticulum Network Stack 1.2.8 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.7 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.8 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.7 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.8 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">
|
||||
@@ -1337,7 +1337,7 @@ those risks are acceptable to you.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=a5753347"></script>
|
||||
</div><script src="_static/documentation_options.js?v=4d6f9085"></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>
|
||||
|
||||
@@ -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.7 documentation</title>
|
||||
<title>Using Reticulum on Your System - Reticulum Network Stack 1.2.8 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.7 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.8 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.7 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.8 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">
|
||||
@@ -1635,7 +1635,7 @@ systemctl --user enable rnsd.service
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=a5753347"></script>
|
||||
</div><script src="_static/documentation_options.js?v=4d6f9085"></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>
|
||||
|
||||
@@ -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.7 documentation</title>
|
||||
<title>What is Reticulum? - Reticulum Network Stack 1.2.8 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.7 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.8 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.7 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.8 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">
|
||||
@@ -504,7 +504,7 @@ network, and vice versa.</p>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
</div><script src="_static/documentation_options.js?v=a5753347"></script>
|
||||
</div><script src="_static/documentation_options.js?v=4d6f9085"></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>
|
||||
|
||||
@@ -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.7 documentation</title>
|
||||
<title>Zen of Reticulum - Reticulum Network Stack 1.2.8 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.7 documentation</div></a>
|
||||
<a href="index.html"><div class="brand">Reticulum Network Stack 1.2.8 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.7 documentation</span>
|
||||
<span class="sidebar-brand-text">Reticulum Network Stack 1.2.8 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">
|
||||
@@ -676,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=a5753347"></script>
|
||||
</div><script src="_static/documentation_options.js?v=4d6f9085"></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>
|
||||
|
||||
@@ -325,6 +325,8 @@ Access permissions can be configured at the group level in the config file or pe
|
||||
|
||||
By default, **no** permissions are granted for anything! You will have to enable the permissions you require to be able to actually *do* something with `rngit`.
|
||||
|
||||
Permissions can be modified by editing the `rngit` config file, individual `.allowed` files on disk, or remotely using the `rngit perms` command.
|
||||
|
||||
### Permission Types
|
||||
|
||||
The following permissions are supported:
|
||||
@@ -478,6 +480,20 @@ Targets can also use short forms:
|
||||
- Repository permissions: `<group_root>/<group_name>/<repo_name>.allowed`
|
||||
- Document permissions: `<group_root>/<group_name>.work/<doc_id>.allowed`
|
||||
|
||||
## Identity & Destination Aliases
|
||||
|
||||
To make permission and remote destination management easier, you can locally define aliases for commonly used identity and destination hashes. Identity aliases used in permissions resolution can be defined in the `[aliases]` section of the `~/.rngit/config` file, while destination aliases are defined in the `[aliases]` section of the `~/.rngit/client_config` file.
|
||||
|
||||
All alias definitions take the form of `aliased_name = HASH`:
|
||||
|
||||
```text
|
||||
[aliases]
|
||||
alice = d09285e660cfe27cee6d9a0beb58b7e0
|
||||
bob = ffcffb4e255e156e77f79b82c13086a6
|
||||
```
|
||||
|
||||
**Aliases are always resolved locally!** If for example you fork a repository with `rngit fork rns://bobs_node/public/repo_name rns://my_node/forks/repo_name`, the forked repository will of course still reference the full, original destination hash, and use this for subsequent upstream syncs.
|
||||
|
||||
## Serving Pages Over Nomad Network
|
||||
|
||||
In addition to providing Git repository access via the Git remote helper protocol and command-line tools, `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.
|
||||
@@ -543,6 +559,8 @@ def hello_world():
|
||||
```
|
||||
```
|
||||
|
||||
You can use `rawmu` code blocks to render raw Micron inside Markdown files. If you create a code block with the language hint `rawmu`, everything inside it will be treated as Micron directly.
|
||||
|
||||
### 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.
|
||||
|
||||
@@ -209,6 +209,7 @@ to participate in the development of Reticulum itself.
|
||||
* [Permission Examples](git.md#permission-examples)
|
||||
* [Permission Short Forms](git.md#permission-short-forms)
|
||||
* [Permission Configuration Locations](git.md#permission-configuration-locations)
|
||||
* [Identity & Destination Aliases](git.md#identity-destination-aliases)
|
||||
* [Serving Pages Over Nomad Network](git.md#serving-pages-over-nomad-network)
|
||||
* [Enabling the Git Page Node](git.md#enabling-the-git-page-node)
|
||||
* [Accessing Repository Pages](git.md#accessing-repository-pages)
|
||||
|
||||
@@ -348,6 +348,8 @@ Access permissions can be configured at the group level in the config file or pe
|
||||
|
||||
By default, **no** permissions are granted for anything! You will have to enable the permissions you require to be able to actually *do* something with ``rngit``.
|
||||
|
||||
Permissions can be modified by editing the ``rngit`` config file, individual ``.allowed`` files on disk, or remotely using the ``rngit perms`` command.
|
||||
|
||||
Permission Types
|
||||
----------------
|
||||
|
||||
@@ -509,6 +511,21 @@ Permission Configuration Locations
|
||||
- Repository permissions: ``<group_root>/<group_name>/<repo_name>.allowed``
|
||||
- Document permissions: ``<group_root>/<group_name>.work/<doc_id>.allowed``
|
||||
|
||||
Identity & Destination Aliases
|
||||
==============================
|
||||
|
||||
To make permission and remote destination management easier, you can locally define aliases for commonly used identity and destination hashes. Identity aliases used in permissions resolution can be defined in the ``[aliases]`` section of the ``~/.rngit/config`` file, while destination aliases are defined in the ``[aliases]`` section of the ``~/.rngit/client_config`` file.
|
||||
|
||||
All alias definitions take the form of ``aliased_name = HASH``:
|
||||
|
||||
.. code:: text
|
||||
|
||||
[aliases]
|
||||
alice = d09285e660cfe27cee6d9a0beb58b7e0
|
||||
bob = ffcffb4e255e156e77f79b82c13086a6
|
||||
|
||||
**Aliases are always resolved locally!** If for example you fork a repository with ``rngit fork rns://bobs_node/public/repo_name rns://my_node/forks/repo_name``, the forked repository will of course still reference the full, original destination hash, and use this for subsequent upstream syncs.
|
||||
|
||||
Serving Pages Over Nomad Network
|
||||
================================
|
||||
|
||||
@@ -578,6 +595,8 @@ Code blocks in Markdown can include language hints for syntax highlighting:
|
||||
print("Hello, Reticulum!")
|
||||
```
|
||||
|
||||
You can use ``rawmu`` code blocks to render raw Micron inside Markdown files. If you create a code block with the language hint ``rawmu``, everything inside it will be treated as Micron directly.
|
||||
|
||||
Customizing Templates
|
||||
---------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user