What a write-only handoff needs to do
Receiving a file from someone usually forces a choice between two awkward options. Either the sender creates an account on a service you both use, or you hand over a credential that can also read everything you have stored. A write-only capability removes that choice, because it grants exactly one verb, which is adding bytes, and nothing that lets the holder look at what is already there.
HTTP already supplies the pieces you need. RFC 9110 defines PUT as storing a representation at a target and the Authorization header as the place to carry a credential. The remaining work is deciding what the credential allows, which is the least-privilege question that the OWASP authorization guidance asks of any access design. For a drop-off the answer is narrow: the sender may deposit, the owner may collect.
Create the box and keep the read capability
The unit you create is a box, a named space that holds uploaded objects, each with its own list of versions. In svc.nz the first upload to a new box creates it, and the response to that upload hands back a read capability, a write capability and a longer-lived owner capability for later administration. During the private trial a new box on the hosted service is created by invitation, so read the policy centre for the current status before planning around it. Once you hold the capabilities, handing out access needs no account on the sender's side.
Keep the read capability and the owner capability to yourself. Give the sender only the write capability, along with the box name and the object name you want them to use. The commands below use placeholders in angle brackets, because a real capability is a secret and belongs in a header, never in a URL, a ticket title or a chat link.
| Action | Write capability | Read capability |
|---|---|---|
| Upload an object (PUT) | Accepted | Refused with 401 |
| Download an object (GET) | Refused with 401 | Accepted |
| List names in the box | Refused with 401 | Accepted |
The table records one run against the repository's own server on a local machine, with one freshly created box and one request per cell, so it shows the intended behaviour rather than a statistical sample. The refusal messages were "Write-only capability cannot read." and "Read-only capability cannot write."
What the sender runs
Anyone with curl can upload using a single command. The file travels as raw bytes, so --data-binary matters: it stops curl from treating the content as a web form.
curl -X PUT -H "Authorization: Capability <write-capability>" \
--data-binary @invoice.pdf \
https://in.svc.nz/<box>/invoice.pdf
Anyone who already has the svcbox client can use its put command instead, with the box, the object name, the file and the capability. The client sends the capability in the same header, so nothing about the secret reaches a URL.
svcbox put <box> invoice.pdf invoice.pdf --cap <write-capability>
Both commands print or return the new version number and nothing the sender can use to fetch the file back. That is the point of the design: a successful upload proves the bytes were stored, and the sender has no way to confirm their content beyond that. If you want them to know it arrived, tell them out of band.
How you read what arrived
With the read capability you fetch the latest version of an object, or pin a specific one when you need the same bytes again. The two commands below were run against a local server with a file the sender had uploaded using the write capability, and the downloaded copy matched the original.
curl -H "Authorization: Capability <read-capability>" \
https://out.svc.nz/<box>/invoice.pdf/latest -o invoice.pdf
svcbox get <box> invoice.pdf latest --cap <read-capability> --out invoice.pdf
The read capability can also list the names in the box and the versions of one name, which is how you find out what several senders have deposited. The svcbox versions command prints each version with its size, type and time. Compare a checksum of the received file with one the sender gives you through a different channel when the content matters, because a capability controls access and says nothing about integrity.
Limits and expiry
Every upload becomes a new version, so a second upload to the same name adds a version and leaves the earlier one readable by you. A sender holding the write capability can therefore add to a name that already exists. If one box serves several senders, give each a distinct object name, and ask them to add an If-None-Match: * header so a PUT to a taken name is refused with a 412 status instead of adding a version. That refusal was confirmed in the same local run.
Boxes created without an account expire, and expiry ends normal access through the API for every capability on the box. It does not promise that the underlying bytes are erased at that moment, so a sensitive file should be encrypted by the sender before upload and the key passed through a separate channel. Collect what you need before the expiry time shown in the response to your first upload.
Size, storage and request limits also apply. This post does not list the numbers, because they belong to the deployment and can change. The response to the first upload reports the limits that apply to that particular box, so check those figures before asking a sender for a large file.
Next, create a box with a small test file, hand a colleague only the write capability and ask them to upload something. Then try a download with that same capability and confirm it is refused, which tells you the handoff behaves as described before a real sender depends on it.
FAQ
Can the sender see what other people uploaded?
Nothing beyond the response to their own upload is visible to them. A write-only capability is refused when it tries to download or list names.
Can the sender overwrite someone else's file?
A second upload to a name that already exists becomes the next version of that name, and earlier versions stay available to the read capability. Give each sender a distinct name, or have them add an If-None-Match: * header so an upload to a taken name is refused.
What if the write capability leaks?
Whoever holds it can add uploads to the box until it expires, but cannot read anything back. Treat it as a bearer secret, send it over a channel you trust for the job and let it expire when the job is done.
Does the sender need svcbox installed?
Any HTTP client that can send a PUT request with an Authorization header works, and curl is the smallest example. svcbox adds a convenient put command for people who already have it.
Sources
HTTP Semantics, the standard reference; OWASP Authorization Cheat Sheet; curl man page.