next #2

Merged
miles merged 23 commits from next into main 2026-11-02 15:57:58 -06:00
Owner
No description provided.
Binks no longer has setting locks (BINKS_LOCK_* / [locks]): runtime
settings are saved in the database and kept as one typed snapshot. The
test server saves its settings with db.set_settings, as an admin would,
right after opening the database; registration stays open unless a test
says otherwise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Binks groups its runtime settings by whom they apply to and moved the
upload hints to startup options:

- The TUS chunk size and parallel streams are startup options
  (BINKS_UPLOAD_CHUNK_SIZE_MB, BINKS_UPLOAD_PARALLEL_STREAMS): tests set
  them through `server_with`, which changes the startup configuration
  before the server starts.
- The anonymous file size limit is anonymous_max_file_size_mb.
- Anonymous end-to-end encryption and short links are off by default, so
  the tests that encrypt or shorten without an account switch them on.

The CLI itself is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Binks now always serves its API documentation at /docs, so the test
server's config has no enable_docs field and AppState::new takes no flag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Binks 0.9 keeps pastes, files and links in one items table, and
Database::update_moderation_status went with the old slugs table. The
moderation-gate test now sets each state as moderation does, with a
compare-and-set transition on the item; the messages it checks are
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Binks 0.9 says api_version 2 in /api/config and moves every route the CLI
uses; against anything else the CLI would fail in confusing ways (a 0.8
server answers the items API with a 404 that reads as "not found"). Every
command but logout now checks the version first and names the server it
needs.

The limits come from the new config: the caller's text and file sizes,
end-to-end encryption and expiries, and the TUS chunk size and parallel
streams. An expiry, a file or encryption the server wouldn't take is
refused before anything is encrypted or sent, with a hint when signing in
would lift the limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Binks 0.9 answers every error with a documented code ("Act on code;
messages may change"), and 0.8's status field is gone. The CLI now keeps
the code, decides on it instead of statuses and messages, and words the
common ones for the command line: a taken ID points at --id, a rate limit
or a busy server says when to try again, and the rest show the server's
message, which says what to do ("The file is too large: at most 100 MB").

Two hints need what only the CLI knows: an anonymous request refused with
sign_in_required points at `binks login`, and a request refused with
unauthorized while it carried an API token (an expired or revoked one,
which 0.9 refuses instead of treating as anonymous) says to get a new one.
A wrong two-factor code at sign-in is said in plain words.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Binks 0.9 creates texts and links with POST /api/items, its options under
one set of names (expire, burn as a number, encrypted, id) and the password
in X-Binks-Password, so it stays out of bodies and logs. Files keep going
over TUS, whose Upload-Metadata now uses the same names; the item's
Upload-Url comes back absolute, and attaching a note answers with the item.

Only the request that finishes an upload carries the deletion token, and
the note's answer has none. 0.8 kept that answer as the result of
`binks paste TEXT -f FILE`, so an anonymous user lost the token and a later
`binks delete` was refused; the token from the upload is kept now.

Results are the server's item: --json prints it, with the URL's #k= key and
the deletion token, and an upload without a note reads its item back for
its expiry and status. Links are always public, so --password and
--encrypted with shorten are refused instead of dropped without a word.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
0.8's CLI guessed what an ID named: a paste, and on a 404 a file. Binks 0.9
says it in the item's metadata (GET /api/items/{id}), which never counts as
a read or burns anything, so `binks get` asks for it first, and a password,
a scan or a used-up item stops it before a read is spent.

Then by kind: a text prints from /{id}/raw; a file's note prints (it never
counts) before the file downloads from /{id}/file, resuming with Range as
before; a link prints its target from the metadata, without a read. A
burn-after-reading link leaves its target out of the metadata, since
showing it is a read, so get reads it from /{id}/raw and says it used one
up; reads of burn-after-reading items say how many are left. `--info` shows
the metadata (the server's item JSON with --json) and never burns.

What keeps an item from being read is told by its code: being scanned
(423, with `--wait` waiting out its Retry-After), held for review, removed,
expired or burned. On a terminal, text that doesn't end in a newline gets
one, so the prompt and status lines start on their own line; piped, the
bytes stay as they are.

The tests read the server through the items API too, as 0.8's routes go
in step 10.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Binks 0.9 deletes with DELETE /api/items/{id}, by the owner's token or the
item's X-Deletion-Token, and keeps the account's routes under /api/me;
0.8's /api/paste and /api/user/tokens go in step 10. With this the CLI
calls none of 0.8's routes.

The refusals are told apart by their code: no credentials (sign in or pass
--deletion-token), credentials that no longer hold (an expired or revoked
API token, refused rather than taken as anonymous), and a wrong deletion
token or someone else's item, with the server's reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Binks 0.8 kept any language text; 0.9 takes only a highlighting language's
name or alias as highlight.js knows it (up to 32 of A-Z, a-z, 0-9 and
+ # . _ -) and refuses anything else with 400 invalid_option. The CLI now
checks -l first and says what it takes, so a typo doesn't cost a round
trip, or an upload's worth of waiting when the text comes with a file.
End-to-end encrypted texts carry their language inside the ciphertext,
where the server can't check it, and -l is checked for them too: the web
app highlights with it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The CLI now talks to Binks 0.9 only, prints the server's item JSON with
--json, takes highlight.js names for -l and reads links and metadata
without using up reads; the README says so. Its recipe for testing against
a local server checkout didn't work when the versions differ (cargo leaves
such a patch unused until the lock names it), so it updates the lock first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The API version check ran against the configured server before every
command, but `binks get https://other.example/abc` talks to the server in
the URL (anonymously; credentials never go to another host). With no
server configured, or an old one, reading a URL failed although its
server was fine. get and delete now check whichever server they talk to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The note is attached after the file is uploaded, and when the server
refused it the CLI returned the error alone: the file stayed on the server,
public, with its URL and deletion token never shown or saved, and running
the command again uploaded a second copy. A blank note (`echo | binks paste
-f f`) failed this way every time, since the server refuses an empty note.

A blank note is now left out, as the server's own forms do. A note is text,
so binks checks before uploading what the server will check: that the
caller may create text and that the note is within the text size limit.
And when the server refuses the note anyway (its settings changed during
the upload, moderation), the file is reported like any other, its deletion
token saved, before binks fails saying why the note isn't attached.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
burn_after_reading_large_file never asked for several connections, and on
loopback binks downloads over one anyway, so it passed with or without the
rule that a burn-after-reading file is never split into ranges (each range
would count as a read). It now allows two reads and downloads twice with
--connections 4: split, the first download would use up both.

urls_name_their_own_server checked that deleting through another server's
URL is refused, but its binks had no token to leak. Now it has a token that
owns the text on the URL's server, so a leak would let the delete through;
and a second server notes every request it gets, to show that binks, signed
in elsewhere, sends it no credentials at all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After a download, binks said how many reads a burn-after-reading file had
left by subtracting one from what the metadata said before. But every
request for a burn file's bytes counts as a read, a resumed one too, so a
download that broke off and resumed spent more than one, and binks told
more reads left than there were (with two left, that the file was still
there when it was gone).

It now asks for the metadata again after the download, which is never a
read, and tells the server's count; an item read its last time says so.
Texts and links' targets are read in one request, so their count stays as
it was.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
binks sent the token `binks login` saved to whatever server a run named:
`binks --server http://other get abc` or `paste` gave other's
/api/config and /api/items the owner's Bearer token, an `all` token that
may be a superadmin's. logout and login with --server mixed servers the
same way, revoking a token ID on a server it doesn't belong to.

The saved token now records the origin of the server it was made on
(login, register, `token create --save`) and goes only there; another
server gets no token, with a note saying so. A token given for the run
(--token, BINKS_TOKEN) is used as given. A token saved before this is taken
to be for the server binks is set up for (the saved server, else
BINKS_URL, else the default), and that is recorded. logout, and login
replacing an older token, revoke it on its own server with itself; `binks
config set token` forgets the old token's ID and server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The URL binks prints is the server's public one, which needn't be the URL
binks talks to (localhost against 127.0.0.1, a proxy's name). get and
delete compared the server a URL names with binks's own as strings, so
the printed URL could count as another server's: read anonymously, and
deleted without the deletion token, which was kept under the URL binks
used. A URL written in another case (HTTPS://…) wasn't even taken for a
URL.

Servers are now compared by origin (scheme, host and port, normalized),
URLs are recognized whatever the case of their scheme, and a new item's
deletion token is kept under its URL's origin as well as under the server
binks used (deleting it forgets both). Tokens saved by earlier versions are
still found.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A TUS upload's Location (and so its PATCH, HEAD, final join and note)
was followed wherever it pointed, with the client's Bearer token: a
server, or anything able to answer in its place, could send binks and the
token to another host.

Client::url, which every request goes through, now takes paths, and
absolute URLs only on its server's origin; anything else is refused with
an error naming both hosts. A Location is checked as soon as it arrives,
so a refused upload stops cleanly and leaves nothing to resume.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
errors_say_what_to_do spent the anonymous upload budget of 3 with an
upload the settings refuse, two texts and a refused ID. Binks 0.9 now
checks a TUS upload against its settings before it charges the budget
(a refused upload no longer costs a client its hour), so the refused
upload doesn't count: a budget of 2 is what the two texts spend before
the third is told to wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
binks-cli now talks to Binks 0.9 only, and its version says so: 0.9.0
for the 0.9 server, as 0.8 went with 0.8. The changelog is new; it
lists what users will notice since 0.8.1 (the server it needs, the
item JSON of --json, how get reads by kind, the checks made before
sending, the fixes to deletion tokens and refused notes, and where the
saved token and requests may go) and what stayed the same. The README
says that a saved login token only goes to its own server, and lists
the token_server key. The binks rev the tests pin is bumped once the
server is pushed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`binks --json get <id> --info | head` (or any reader that exits early)
made binks panic with "failed printing to stdout: Broken pipe" and exit
101, because Rust ignores SIGPIPE. binks now restores SIGPIPE's default
action first thing, so it stops quietly like other Unix tools (exit 141
in the shell).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Build and test against Binks 0.9.0
Some checks failed
CI / Cargo Audit (push) Failing after 19s
CI / Rust (push) Successful in 3m59s
f15c66340c
The end-to-end tests start a real Binks server from the binks crate, and
binks-cli now speaks only 0.9's API, so pin binks to the 0.9.0 commit on
next (4f62179). With the old 0.8.1 pin the tests (and CI) would run
against a server this CLI refuses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Update the dependencies and build against the Binks v0.9.0 tag
All checks were successful
CI / Cargo Audit (push) Successful in 22s
CI / Rust (push) Successful in 3m8s
CI / Cargo Audit (pull_request) Successful in 21s
CI / Rust (pull_request) Successful in 56s
a126df2c57
cargo audit failed on the same advisories as the server's: rustls
0.23.45, rustls-webpki 0.103.15, webpki-root-certs 1.0.9 and ctutils
0.4.3, all fixed in compatible releases. Every direct dependency now
requires its latest release (arboard 3.7, tokio 1.54, actix-web 4.18 for
the tests), the lock is refreshed, and binks is pinned to the v0.9.0 tag
(fc89b8f), whose own dependencies were updated the same way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
miles merged commit d5ed62c9db into main 2026-11-02 15:57:58 -06:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
miles/binks-cli!2
No description provided.