Skip to content

v0.1.21

Choose a tag to compare

@captainpacket captainpacket released this 10 Oct 14:47
· 35 commits to main since this release

[0.1.21] - 2026-10-10

Added

  • snapshots.upload() streams its archives from disk. It read each whole into memory,
    and the multipart body then held them again, so a multi-gigabyte archive cost twice
    its size. A 400 MiB upload now moves the process's peak memory by 3 MiB. Retries are
    unchanged: an upload is still retried only on a connection error, before any of the
    body has gone out.
  • Progress for the calls long enough to look hung, all optional, all plain functions
    called synchronously, and a callback that raises is logged and ignored rather than
    aborting the transfer: upload(on_progress=) and export(on_progress=) /
    download(on_progress=) take (bytes_done, total_or_None), and
    wait_until_processed(on_poll=) takes (snapshot, elapsed_seconds).
  • snapshots.collect(): start a network collection, wait for the task to succeed, and
    return a CollectionRun with the new snapshot id. The collector task carries no
    snapshot id, so it is found by comparing the network's newest snapshots before and
    after, ignoring predictions. A failed, timed-out or cancelled task raises
    ForwardExecutionError; wait=False returns once queued; on_poll(task, elapsed)
    reports the task. Replaces the start-and-poll loop field scripts were writing. Forward
    marks the task succeeded and creates the snapshot in the same step, so the post-success
    wait for the snapshot is short; it can create one even when every device failed and the
    task ended FAILED, which the error does not look for. Checked against Forward's source
    and recorded response shapes, not a live collection, which would change a real network.
  • A device's own collector, a preview: collectorId is declared on classic devices
    and on CLI, SNMP and HTTP endpoints (create, patch and read), so it is typed on a
    read and documented for a write. Forward binds it but leaves it out of its API
    description; the Go SDK session found it in the server source. The value is the
    collector's number, "C3" or "3". To add fields to schemas Forward publishes
    without redefining them, spec/unpublished.yaml has an x-extend-schemas
    section, and the build fails if Forward ever publishes one of those fields, so an
    entry cannot go stale. Reads carry it only when a collector is set (the read response unwraps a metadata
    record that omits it when null; confirmed in source, not seen live because no device on
    the network I could read had a collector set).
  • Listing properties, unpublished: configuration.get_org_config_values(),
    get_global_config_values() and get_org_scoped_config_values(org_id=), each with
    the five filters Forward defines (OFF, CONFIGURABLE, CONFIGURED,
    OVERRIDDEN, NONDEFAULT) and no default of their own, since the routes disagree
    about it. describe_properties() combines three listings into each property's
    effective value and its source (org, global or default); Forward reports no
    source itself. Verified live: 15 org, 6 global and 175 default out of 196.
  • snapshots.export() and download() take include_devices, exclude_devices,
    obfuscation_key and obfuscate_names, selecting Forward's subset export. That
    operation was in the API description and had a request builder, but nothing on the
    client reached it. Verified live: one device's subset was a real 51 MB, 72-file
    archive, and Forward's refusal of an empty subset surfaces as a 400.

Fixed

  • snapshots.export() and download() failed with HTTP 406: no response body on some
    Forward builds. The SDK asked for application/octet-stream; those builds answer 406 to
    that and to application/zip, and serve */* or no Accept header. The API description
    names no media type for the export, so the SDK now asks for anything. Found by the ATOS
    field script, which had been patching the constant at runtime. Reproduced only on their
    build: the ADP network accepted either header. The three raw-file reads
    (snapshots.file(), snapshots.exceptions_text() and devices.file()) asked for
    text/plain with only JSON as the alternative; they now prefer text and accept
    anything, as a precaution for the same reason. That was not seen to fail.
  • A 406 with no body now says what was refused (Accept: <header>) instead of
    "no response body".