Skip to content

Releases: afit21/rnfs

rnfs 0.2.1

Choose a tag to compare

@github-actions github-actions released this 24 Oct 05:05
3cf1142

Fixes, and additions to the local API for apps. No wire-format or CID changes: these nodes, 0.2.0, 0.1.6, 0.1.5,
0.1.4 and 0.1.3 nodes work together. Every API addition is additive: older clients see the answers they knew, with
new fields beside them, and GET /api/capabilities lists gc_dry_run, config_dry_run, add_origin,
sealed_tar, ping_details, streamed_answers and restart.

Added (the local API, rnfs.Client/rnfs.Node, and the CLI where it helps):

  • A peer's real last sign of life: GET /api/peers (Peer.last_seen) gives when each peer last showed it was
    up (an answer, a request of its own, a link, a fresh announce), as wall time; it is what online is decided
    from. last_heard is no such sign: any node's path response refreshes it.
  • Why a ping failed: GET /api/ping (ping_info(), Ping) adds reason (no_path, no_identity,
    wrong_identity, not_member, link_refused, link_closed, link_timeout) and the path's hops,
    interface and path_since; stream=1 answers in JSON lines, telling of each step (path, identity, link) as it
    begins. rnfs ping says why.
  • Providers as they are found: GET /api/providers?stream=1 sends each provider as the lookup finds it
    (providers(on_provider=)); closing the connection ends the lookup. rnfs providers prints them as they come.
  • A stream add's origin: POST /api/add/stream takes origin (add(..., origin=), add_tar(..., origin=)),
    what pin ls says it was added from, so an app's adds aren't all (stdin) and the user's label stays free.
  • Sealed folders from a tar upload: POST /api/add/stream?format=tar with to/to_self seals the folder the
    tar holds as add --to <folder> seals one (name is the folder's), re-packing its members as it streams: nothing
    but the sealed bytes is stored.
  • The shared folder's figures: GET /api/status (Status.shared_folder) gives its path, entries and their
    bytes, whether a scan runs, when the last ended, why the folder is refused or the last scan failed, and the
    entries that failed to import; rnfs status sums it up.
  • What gc would free: POST /api/gc with dry_run (gc(dry_run=True), rnfs gc --dry-run) changes nothing
    and says what it would remove, and the stored bytes split by what keeps them: pins, cached fetches, operations in
    progress, nothing, with what the state database takes. A client checks the gc_dry_run capability first: an
    older daemon would ignore dry_run and collect.
  • Settings checked without writing them: POST /api/config/set with dry_run (config_set(..., dry_run=True)) makes every check and says what would apply now and what at the next start (capability
    config_dry_run, for the same reason).
  • How the daemon runs: daemon.json gains started_by (systemd, launchd, windows_task, terminal,
    unknown, or what the program starting it set RNFS_STARTED_BY to), service, sandbox (flatpak) and
    can_restart.
  • Restart through the API: POST /api/restart (restart(wait=), rnfs restart) stops the daemon as SIGTERM
    does and starts it again with the same command line, so settings that need a restart apply to a daemon started
    in a terminal as much as one a service runs. On POSIX it starts again in place, keeping its process ID; on
    Windows the task wrapper (contrib/install-windows-task.ps1, which now sets RNFS_STARTED_BY) restarts it.
  • The routing table: GET /api/routing (routing(), Routing) gives the buckets and each node's times and
    failed queries, the nodes avoided as unreliable, and the bans with when they end and why.
  • The daemon's log: GET /api/log (log()) gives its recent lines, rnfs's and Reticulum's as its console shows
    them (the last 2000, in memory), so an app shows the log of a daemon it didn't start.
  • Uploads are jobs: a stream add or import (POST /api/add/stream, /api/import/stream) is listed in
    GET /api/jobs while it runs (stream, stream_name), with the bytes read, and cancelling it stops it (409,
    code cancelled). Uploads are never saved or resumed, and take no job slot.
  • More events: config (a setting changed through the API, with what applied, what needs a restart and every
    setting pending one), name_sync (a sync started, stopped or changed), apps (added, removed, requested,
    denied) and status (the status fields that changed, checked every 30 s), so a status page needn't poll.
  • Pairing: an app with no token asks for one (POST /api/apps/request; rnfs.Client.pair()), shows its user
    a 6-digit code, and the user approves it where that code shows too (rnfs app requests, rnfs app approve,
    deny; an app told by the requested event). The app collects its token itself, once; the approver never sees
    it.
  • Reticulum's interfaces, read-only: GET /api/stats (Stats.reticulum_interfaces, rnfs stats) gives each
    interface's state, mode, bitrate, bytes and speeds (the shared instance's when attached to one). rnfs still never
    edits Reticulum's config.
  • The code checked at approval: POST /api/apps/approve takes code (ApiConnection.app_approve(id, code=),
    rnfs app approve <id> --code), and rnfs app approve at a terminal asks for the code the app shows. Without it
    the request is approved unchecked, as before.

Changed:

  • Sizes are written in decimal units everywhere (KB, MB, as rnfs jobs, rnfs peers and the log already
    wrote them): rnfs status's record store line was in KiB and MiB. One function writes them all
    (rnfs.units.human_size).
  • The API and the gateway also listen on [::1] (AUD-H2, below), on the same ports: a port another program
    holds on [::1] stops the daemon, as one taken on 127.0.0.1 does. Only a host without IPv6 runs on
    127.0.0.1 alone. [::1] is still not an accepted Host.
  • pin add, remote pins, following, dir set and dir rm stop at max_output_entries entries (AUD-M6, below),
    as get and export did: pinning a folder of more entries (100,000 by default) is refused; raise the setting to
    pin it.
  • Pairing: a name may have several requests waiting, each with its own code; approving one denies the others,
    and a name already taken is refused when approved, not when asked. A full table of 32 requests drops the oldest
    instead of refusing new ones (AUD-M5, AUD-L1, below).
  • The network size estimate is sampled by lookups the node chooses (AUD-M1, below): up to 8 lookups of random
    keys after joining, then one while the estimate is short of samples or an hour old (paced on slow nodes),
    besides publishing's.

Security (the 0.2.1 audit, AUD: each fix has a regression test in tests/security, listed in its README):

  • Links waiting out upload pacing are bounded again (AUD-H1, a 0.2.1 regression; specification section 3): an
    answer that fits one packet took no response slot even while it waited for upload pacing, holding its link's
    /blocks slot, and a link with one in progress is never closed to make room. So max_incoming_links links
    looping on tiny paced answers (about 8 KB/s) had every new incoming link refused. An answer that must wait now
    takes a pool slot, as in 0.2.0, and one outside the pools ends when its link closes.
  • The gateway's sites and the API can't be taken over on [::1] (AUD-H2, section 11): the daemon listened on
    127.0.0.1 only, but browsers and curl try *.localhost and localhost on ::1 first, so another local user
    listening on [::1]:<port> while the daemon ran could serve their own pages in every site's origin and relay
    what clients sent. The API and the gateway now also listen on [::1]:<port>, sharing their connection cap.
  • add can't be led into the rnfs dir by a path swapped while it runs (AUD-H3, section 8): add checked the
    path against the rnfs dir, then opened it again following a link, so a file or folder swapped for a link to
    api_token, the identity or a name key between the two was added, pinned and provided. The check is made again
    on what was opened (its own final path), for plain, sealed and only-hash adds.
  • A node asked first can't hold off the provider search by dripping its answer (AUD-H4, section 7): once a probe
    runs past deadline_slack (longer over slow links) the search starts, and a provider it finds is asked for the
    block as end-game duplication does; a probe waits for its link's /blocks slot no longer than that, so another
    fetch's dripping response can't make later fetches fail without searching; and a remembered provider whose probe
    was cancelled, or ran that long without sending anything, is forgotten.
  • Searches no longer feed the network size estimate (AUD-M1, section 6): anyone can make a node search keys of
    their choosing, through the gateway for one, and 9 keys inside one tight cluster of identities inflated the
    estimate up to a million times, which turned the density check off. Samples come from publish lookups and from
    lookups for random keys; a key the density check flags loses its sample, and widened lookups take none.
  • A fetch's searches share one /find cap again (AUD-M2, section 6): runs of a lookup together send at most
    8 × K /find, and a run counts as having found providers only if the fetch could use one. Sybils listing
    made-up providers in every answer had one fetch send 66,426 /find in 15 s.
  • Partition mode's probe doesn't go to the server that named the failing identities (AUD-M3, section 6) while
    another target exists, and prefers senior servers; a probe left unanswered doesn't enter partition mode if
    another off-island server answered or announced itself while it was out.
  • Links posing as slow can't hold the slow pool for good (AUD-M4, section 3): two links on a fast path that kept
    their MTU at 500 and asked again the moment each response ended held both slow-pool slots, and a LoRa requester
    ...
Read more

rnfs 0.2.0

Choose a tag to compare

@github-actions github-actions released this 22 Oct 12:05
70d2243

The feature-complete release: the local API and the library for companion apps; providing at scale (sweep
providing, provider records on disk within a byte budget, storage tiers against floods); finding content when
identities are generated next to its key (the density check, disjoint lookup paths, the announce first-seen
log); LoRa islands (fast nodes sparing them, two-level records, partition mode); and new features (per-app API
tokens, encrypted folders, NomadNet add, gateway sites, name sync, name key backups, remote pin status, network
status, files inside folders provided by their own CIDs). LXMF messaging moved out of rnfs into an app of its own
(Removed, below). The wire format gained one optional request, /range (Added, below), which these nodes send
only to nodes that answer it; nothing else on the wire changed, and CIDs are unchanged: these nodes, 0.1.6, 0.1.5,
0.1.4 and 0.1.3 nodes work together (tests/interop.py against 0.1.6 passes). The local API stays compatible but
for LXMF: every route, field and status of 0.1.6 is unchanged but LXMF's, which are gone, and error bodies gain a
field. The upgrade of an <rnfs dir> is automatic and one-way.

Upgrade notes:

  • State: the state database gains tables (pin_times, pin_labels, name_exports, apps, block_sizes,
    routing, peer_scores, announce_first_seen, provider_standing, remote_pins_asked, remote_pins_served,
    name_syncs, name_sync_files, published_heal, name_announces, remote_pin_retries, range_support,
    publish_items, publish_stores); an older rnfs can't open it afterwards. The publisher's published and
    published_earlier rows move into publish_items (a row per provided item), leaving those tables empty. Pins
    made before the upgrade have no added time, name keys made before it count as never exported (rnfs status
    lists them until rnfs name export is run), and remote pins asked before it have no status entry.
  • LXMF: rnfs no longer sends or receives LXMF (Removed, below). Pins of links auto-fetched over LXMF change
    source from lxmf:<address> to app:lxmf:<address> (pin ls shows the new one), an app called lxmf's, so
    they stay pinned for an LXMF app to take over; nothing is released by the change, and an older pins.json
    imported at the upgrade gets the same rewrite. storage/lxmf_identity, lxmf/ and the inbox and outbox in
    rnfs.db are kept, untouched. lxmf_* settings in rnfs.toml are ignored with a warning. If you rely on
    rnfs send and rnfs inbox, don't upgrade before an LXMF app is there: the upgrade can't be undone.
  • Provider records: the upgrade gives each publisher of the records a node stores a standing
    (provider_standing, and indexes on provider_records): Earned if it was first seen at least 3 days before its
    latest store and refreshed a record outside the newcomer pool a day or more after storing it, else Newcomer. The
    newcomer pool's records are no longer pooled, and the remembered departed and evicted publishers are dropped.
  • max_records is retired: a max_records left in rnfs.toml (or given to rnfs.Node) is ignored with a
    warning instead of stopping the daemon; record_budget (bytes) replaces it. rnfs config set max_records is
    refused as an unknown setting; rnfs config unset max_records removes it.
  • Blocks: they move from blocks/<cid> into 1,024 subdirectories, blocks/<xy>/<cid> (the CID's third- and
    second-to-last characters), at the first start, which takes a while on a large store (a rename per block; the API
    answers once it is done: 6 minutes for 883k blocks on a desktop SSD, and 6 for 857k on a 2-vCPU cloud VM, of which
    110 s the move; the log says how far the listing and the move got about every 30 seconds, and, if they take over 10
    seconds, the listing of every block that follows and the first working out of what the node provides). Each start
    moves whatever is left, so an interrupted upgrade loses nothing, and a block Windows won't let rnfs move stays
    readable until a later start moves it. An older rnfs doesn't find the moved blocks. A subdirectory of blocks/ that
    is a link, not a directory or another user's stops the start. The state database gains a block_sizes table, every
    stored block's size, which the first start fills by listing the blocks.
  • <rnfs dir>/daemon.json: the daemon writes it while it runs. Clients from 0.2.0 on read the API port from
    it first, so one that can't read a newer daemon's rnfs.toml still finds it.
  • Settings: rnfs.toml may now be rewritten by the daemon (rnfs config set, POST /api/config/set). Only the
    assignments changed are replaced; comments and other lines are kept. Settings whose api_port equals a
    non-zero gateway_port are now refused at start (such a daemon could never listen on both).
  • Routing state: the state database gains tables for the routing table (routing), which is now kept across
    restarts, for local scores (peer_scores) and for the announce first-seen log (announce_first_seen).
  • Files and folders inside pins are provided (provide_entries, below, on by default): after the upgrade a node
    that isn't slow also publishes the CID of every file and folder inside its own complete pins (not remote pins or
    followed names), up to provide_entries_max (100,000), so its first rounds publish more keys. Set
    provide_entries = false to keep 0.1.6's behaviour.

Security:

  • A provider can't keep a fetch alive for ever by alternating unreachable and unestablished links (REL-1): a "no
    path" result (retried once, a minute later) reset the count of lost requests, and a link that didn't establish
    (lost) re-armed the retry, so a provider whose results alternated the two, as a node ignoring link requests and
    answering every other path request gets, never failed, and its fetch, thread and searches lived as long as it
    liked. Only an answer, or an outright failure, resets the lost count now.
  • A request for nothing served can't cancel a download (REL-2, specification section 3): a /blocks or /range
    request took a response slot, displacing a response in progress when the pool was full, before the node asked
    whether it serves anything requested. One ~100-byte request for a made-up CID from a fresh link cancelled an honest
    slow-link download, and repeating it broke every one. Such a request is now answered at once, with the same answer
    as before, without a slot.
  • A request of the wrong shape is dropped before RNS indexes it (REL-3): RNS reads a request as
    [time, path hash, data] unchecked, so one whose plaintext was an int, a short list or had a list as its path
    raised on RNS's request thread, a traceback in the log for every such packet, before rnfs's per-link rate limit
    saw it. The guards now drop it with a throttled verbose line; no handler could have served it.
  • A tar member of negative size is refused by rnfs itself (REL-4): Python's tarfile parses a negative size from
    a base-256 field or a pax size= record, and only interpreters with its own offset check (3.12.3 and later fixes)
    refused such a stream; rnfs supports 3.11. Sealed folders and tar adds now refuse it on every interpreter.
  • A file in the rnfs dir can't be added through another name for it (REL-8, completing REV1): the check that add
    refuses the rnfs dir compared paths, and a file added as the add's root isn't walked (the walks skip the rnfs dir by
    inode), so on a case-insensitive disk (macOS: ~/.RNFS/api_token) or through a bind mount an app could add the
    CLI's token, the node identity or a name key and read it back. The file's directories are now compared by device and
    inode too, up to the rnfs dir's files/.
  • Gateway documents are revalidated, not cached as immutable (REL-9, specification section 11): every /rnfs/
    response was immutable for a year, though its Content-Security-Policy follows gateway_scripts, so a page cached
    while scripts were allowed kept running them in the browser after they were turned off. HTML, SVG, other XML, PDF
    and listings now carry Cache-Control: public, no-cache (the browser keeps them and asks again, a 304 from the
    node), and a 304 carries no Cache-Control. Other files stay immutable.
  • Withdrawing and restoring the same records spends no write budget (REL-13, completing REV2): a withdrawal
    spent a row of the provider write budget and a restore before the next save spent nothing but gave nothing back,
    though neither row is then written. One identity looping over its own 27 records spent the budget other publishers'
    new records need, while writing almost nothing, and every other Newcomer's new record was refused (an Earned one's
    too, from an Earned attacker). The row a withdrawal spent is now given back when it is undone.
  • A keeper can't make this node keep and serve a name below the sequence it knows (REL-14): the keeper store's
    rollback check, and the sequence a keeper query checks answers against, left out the name floor (the highest
    sequence of a name resolved before, kept after its record left the table). A lying keeper's old, validly signed
    record of such a name, once kept, was served onward by this node. The floor counts now.
  • A flood of fresh identities can't set the clock-offset estimate (REL-15, specification section 9): it was the
    median of the 128 senders heard most recently, so 128 announces of fresh identities emitted 2 h ahead pushed every
    honest sender out, and the node warned that its correct clock was 2 h behind. Once 5 senior servers were heard the
    estimate is theirs alone, and the table forgets other senders first.
  • What fuzzing the gateway, the API, settings and tar streams found (REL-17): a web page could set any
    Content-Type, control characters included, with the gateway's ?filename=data:<type>, (the type is now taken from
    the name's exte...
Read more

rnfs 0.1.6

Choose a tag to compare

@github-actions github-actions released this 18 Oct 05:38

Security fixes, maintainability work and performance work. No wire-format or CID changes: these nodes, 0.1.5,
0.1.4 and 0.1.3 nodes work together. The upgrade of an <rnfs dir> is automatic and one-way.

Upgrade notes (each is described in full below):

  • Settings and the rnfs dir: a symlinked rnfs.toml, or one or a subdirectory owned by another user, stops the
    start; a loose one (such as a 0664 rnfs.toml or 0775 blocks/) is made owner-only, with a warning.
  • The shared folder: one that leads into the rnfs dir (or a directory holding it) is refused, and its
    pins with source folder are released.
  • Following names: a followed name's target may take at most follow_quota (new, default 1 GiB).
  • Deny lists: while the deny list has sha256: entries, a file repeating its content more than 16 times
    (beyond 64 MiB) can't be read or imported (451).
  • Local API: POST /api/lxmf/inbox/get answers 404 for a name link whose name can't be resolved, unless the
    request sets use_sent_cid.
  • Gateway: browsers that send no Sec-Fetch-Site (Safari before 16.4, Firefox before 90) are treated as web
    pages, so ?format= downloads give them 409.
  • rnfs inbox: its message line is reordered (address and trust first, the display name last, quoted).
  • install.sh: it stops when the GitHub CLI can check provenance but isn't logged in; log in with
    gh auth login, or set RNFS_REQUIRE_ATTESTATION=0.
  • Flatpak: after updating, the Flatpak no longer sees the rest of your home folder or any device. Grant what
    you used again: flatpak override --user --device=all io.github.afit21.rnfs for an RNode or other serial
    interface, and flatpak override --user --filesystem=<folder> io.github.afit21.rnfs for other folders,
    ~/.config/reticulum included if your Reticulum config is there.
  • State: the state database gains a table (the shared folder's file cache); an older rnfs can't open it
    afterwards.

Security:

  • The release workflow's Flatpak build no longer uses the GitHub Actions cache. Any workflow run on the default
    branch can write that cache, and a poisoned one would have gone into the attested .flatpak bundle.
  • rnfs inbox get no longer quietly fetches the CID a sender put in a name link when the name can't be
    resolved. The sender chose that CID and nothing ties it to the name, so any sender could pose as a
    well-known name. It now fails before writing anything, unless you pass --use-sent-cid; then the result
    carries a warning. Auto-fetch (trusted senders only) keeps the fallback, and records it.
  • A /blocks requester that rejects or cancels a response before a quarter of it was sent now cools down for
    blocks_cooldown, per link and per identity, as one that rejected it unsent already did. Before, a requester
    could take the first window of each response, cancel it and ask again at once, so the node built a whole
    response per window. Closing the link still starts no cooldown, so an honest reader that gives up early isn't
    held back.
  • While the deny list has file hashes (sha256: entries), a file that declares more than 16 times the bytes of
    its distinct chunks, plus 64 MiB, is refused (451) before it is hashed. Hashing reads every repeat of
    repeated content, so six blocks (128 KB) declaring 2^56 bytes made a one-byte cat, a gateway range request,
    ?format=archive or an import hash for about a year, holding a gateway slot. cat and the gateway now fetch
    a file's whole DAG before checking it (unless its hash is already known), so max_download_size and the disk
    reserve apply as they do for get.
  • A gateway HEAD (or a range not starting at byte 0) of a stored file the deny list blocks answers 451, like
    GET. Before, it took its Content-Type from a denied stored chunk, and a HEAD of a file with a denied hash
    answered 200.
  • A name whose owner is offline can still be resolved through its keepers when Sybil nodes near its keeper key
    stall the DHT lookup. They could answer /find slowly with ever closer IDs, so the search used all of
    lookup_timeout and the keepers then had one second to answer. Each batch of /name queries now has its own
    budget (87.5 s at the defaults); the search for keepers stops once some are known and lookup_timeout / 5 has
    passed, and resumes if their answers don't settle the name.
  • A neighbour replaying an older, validly signed name announce no longer makes its record look current: rnfs
    reads the replayed announce's own emission time from RNS's announce cache, treats one older than the path's
    (or carrying another record) as a suspected replay, gives its real age and asks keepers.
  • A provider can no longer hold a fetch for hours by dripping a /blocks response: once nothing is left to
    request and a provider is idle, a batch in flight longer than deadline_slack is asked of it too, the first
    verified copy wins, and the slow request's link is closed (left to finish if another fetch uses it) without
    counting as a stall. Simulated: 20.6 → 3.3 s.
  • get of a directory can no longer be redirected by someone who can rename inside the output swapping one of
    its directories for a link or junction (or another directory) while it is written. On Linux and macOS the
    output being written is private (0700, setgid kept) and must start empty, every entry is created relative to
    its parent's descriptor without following links, and each directory is checked by device and inode before
    anything is written into it. On Windows it gets a private access list while it is written, and each file's
    final path must be inside it. A swap fails the get with "output was changed while being written".
  • On Linux and macOS, rnfs.toml is now checked before its settings are used, as on Windows: a symlink, a FIFO
    or anything else that isn't a regular file is refused, and so is a file another user owns; one others may
    access is made 0600, with a warning; at most 1 MiB of it is read. Its settings choose the Reticulum config, and
    with it programs rnfs starts, so anyone who could write it could run code as you. Reticulum's config gets the
    same check when reticulum_dir is the rnfs dir.
  • The rnfs dir's subdirectories (state, blocks, storage, names, lxmf) are checked at every start: a
    symlink, a non-directory or one another user owns stops the start before Reticulum starts; a loose one is made
    0700, with a notice; and if others could write to it, entries another user owns are reported. Before, a user
    who could once write in a loose rnfs dir kept their access to blocks/ and names/ after it was tightened.
  • The shared folder is no longer scanned when files/ leads into the rnfs dir. Before, a symlinked files/ that
    led to storage/ or names/ (or that someone who could write to a directory above it retargeted there)
    imported, pinned and provided the node's identity or the name keys, and one that led to the rnfs dir or a
    directory above it shared everything in it. A folder that resolves, as opened, to the rnfs dir, a directory
    holding it, or anything inside storage/, names/, state/, blocks/ or lxmf/ is now refused with a
    warning and scanned as empty: nothing is imported, and every pin with source folder is released, so keys a
    0.1.5 node already shared that way stop being provided at the first scan after the upgrade. On Linux and macOS,
    a directory above the folder that other users can change gets a warning, once.
  • A node no longer reads request packets sent to it on rnfs.dht links it opened. That end of a link has no
    request handlers, yet RNS decoded each request (up to the link's packet size) and started a thread for it
    before finding nothing to run, for any node rnfs linked to. LXMF and host-program links are unchanged.
  • An explicit max_upload_rate now applies to every /blocks response, including one that fits in one packet.
    Such responses never give the link a rate estimate, and the cap applied only once there was one, so on a
    large-MTU path (RNS's TCP interfaces) a requester asking only for single-packet answers was never paced. Waits
    of up to 5.6 s are held and longer ones refused as busy, as for larger responses. The default cap (half the
    rate estimate, on slow links) is unchanged.
  • The wait for a previous /blocks response's proof uses the round-trip time the requester claims only if it is
    a finite, non-negative number, and still at most 5 s. Before, a negative claim gave an honest proof no grace,
    and a value that wasn't a number made the request handler fail.
  • install.sh ties a release install to the tag it asked for: where gh supports them, the provenance check
    passes --source-ref=refs/tags/<tag> and --deny-self-hosted-runners, and only an rnfs-<version>-*.whl of
    that tag's version is installed. Before, an older release's genuinely attested sdist, renamed to the new
    release's file name, passed both checks.
  • install.sh stops when the GitHub CLI has gh attestation but isn't logged in (root installs usually hit
    this), instead of quietly checking SHA256SUMS only. Log in with gh auth login, or set
    RNFS_REQUIRE_ATTESTATION=0 to install with the checksum only. Without gh it still warns, and the README now
    says that curl | sh without gh trusts whoever can write the release's files.
  • The Flatpak's sandbox is narrowed to ~/.rnfs, ~/.reticulum and the Downloads folder, with no devices
    (before: your whole home folder and every device, so a compromised daemon could write ~/.bashrc). See the
    upgrade note below.
  • install.sh warns when RNFS_SOURCE is, or names, a plain http:// URL, and the .deb's postinst
    byte-compiles with python3 -I.
  • The file-index-node NomadNet page renders at most once a minute, one visit at a time, and serves its cached
    page meanwhile (before, every visit ran rnfs pin ls).
  • examples/rnfs_share.py and rnfs.Client's log escape names and...
Read more

rnfs 0.1.5

Choose a tag to compare

@github-actions github-actions released this 17 Oct 00:50
5bd3075

What's Changed

Full Changelog: v0.1.4...v0.1.5

rnfs 0.1.4

Choose a tag to compare

@github-actions github-actions released this 15 Oct 07:57
ffd2397

What's Changed

Full Changelog: v0.1.3...v0.1.4

rnfs 0.1.3

Choose a tag to compare

@github-actions github-actions released this 13 Oct 10:23
b575a2e

What's Changed

Full Changelog: v0.1.2...v0.1.3

rnfs 0.1.2

Choose a tag to compare

@github-actions github-actions released this 13 Oct 00:07
9f16100

What's Changed

  • Fix tests that fail on macOS and Windows by @afit21 in #1
  • Release 0.1.2 by @afit21 in #2
  • Skip the symlinked-temp test on Windows by @afit21 in #3

New Contributors

  • @afit21 made their first contribution in #1

Full Changelog: v0.1.1...v0.1.2

rnfs 0.1.1

Choose a tag to compare

@github-actions github-actions released this 12 Oct 11:01

Full Changelog: v0.1.0...v0.1.1

rnfs 0.1.0

Choose a tag to compare

@github-actions github-actions released this 11 Oct 05:59