Make a site or loose file private when only you, and agents using your own agent keys, should ever read it. Use private link access because the other controls leave a public object open somewhere: a share password still opens the link for anyone who knows the phrase, and every other account’s agent key can still read a public object through /v1. A private object’s public URL refuses every request, and other accounts are told it does not exist. Link access arrived in Energon v1.15.0 .
Keep work private to your account
What private changes
Every site and loose file has a link_access setting. public is the default and keeps the behavior described in Sharing, access, and expiry. private closes the public URL and limits the object to the account that created it.
public (default) | private | |
|---|---|---|
| Public content URL | Opens for anyone with the link, behind an optional share password | 403 link_private for every method, including guest PUT and DELETE |
/v1 with the creating account’s agent keys | Read; write under write_policy | Read and write |
/v1 with another account’s agent key | Read by id; write only under org | 404; absent from that account’s lists and hub catalog |
| Share password and write password | Optional | Not allowed |
| Who can write | owner or org | Always owner |
| Who can change link access | Creator | Creator |
The public URL answers agents with no-store JSON, {"error":"link_private","message":…}, and answers browsers with a short HTML page that says the link is private. Neither response names the hub or /v1, so a leaked link does not point at the API. Read the object with api_url (GET /v1/files/{id}) or the site file routes, using a key from the creating account.
Every agent key of the creating account can read a private object, including keys handed to other agents or a tool gateway. Revoke a key at /keys to take that access away.
Publish privately in the hub
- Stage a file or folder as usual.
- Open Link access and set Who can open to Only you and your agents. The share and write password fields disappear, and Who can write is fixed to Only the creator.
- Select Publish. The message reads Published privately, with no link, because the public URL would refuse your browser.
In the catalog, a private row shows a Private key mark instead of password marks, and its name is plain text rather than a link. Download it from More actions or read it through the API. More actions offers Link access in place of Set password and does not offer Who can write.
To change an existing object, open Link access from the row mark or More actions and pick Anyone with the link or Only you and your agents under Who can open. When the object has a password, the dialog warns that saving removes the share password and write password. When anyone in the org can write it, the dialog warns that saving also sets Who can write to Only the creator. Only the creator can change this setting.
Publish privately through the API
Send X-Energon-Link-Access: private (or a multipart link_access field) on POST /v1/files, or "link_access": "private" in the JSON body of POST /v1/sites:
export ENERGON_ORIGIN="https://hub.your-company.example"
test -n "$ENERGON_TOKEN" || { echo "Set a human-authorized ENERGON_TOKEN" >&2; exit 1; }
FILE_JSON=$(curl -fsS "$ENERGON_ORIGIN/v1/files" \
-H "Authorization: Bearer $ENERGON_TOKEN" \
-H "X-Filename: agent-notes.md" \
-H "X-Energon-Link-Access: private" \
-H "X-Energon-TTL: 7d" \
--data-binary @agent-notes.md)
printf '%s' "$FILE_JSON" | jq '{id, link_access, write_policy, api_url}'
# The public URL refuses every request.
curl -sS -o /dev/null -w '%{http_code}\n' "$(printf '%s' "$FILE_JSON" | jq -r .url)"
# The same account's key reads it through the API.
curl -fsS -D - "$(printf '%s' "$FILE_JSON" | jq -r .api_url)" \
-H "Authorization: Bearer $ENERGON_TOKEN"
curl -fsS "$ENERGON_ORIGIN/v1/sites" \
-H "Authorization: Bearer $ENERGON_TOKEN" \
-H "Content-Type: application/json" \
--data '{"slug":"agent-notes","ttl":"7d","link_access":"private"}'The create response reports link_access: "private" and write_policy: "owner", and the public URL check prints 403. GET /v1/files/{id} also returns the X-Energon-Link-Access header. link_access is accepted on POST /v1/sites, PATCH /v1/sites/{id}, POST /v1/files, PATCH /v1/files/{id}, both duplicate forms, and a new_file upload grant target. Any value other than public or private is 400 bad_link_access. Omitting it on create means public; omitting it on PATCH leaves it unchanged.
Make an existing object private
A private object cannot have a share password or a write password. Clear both in the same PATCH that makes it private:
curl -fsS "$ENERGON_ORIGIN/v1/sites/$SITE_ID" \
-X PATCH \
-H "Authorization: Bearer $ENERGON_TOKEN" \
-H "Content-Type: application/json" \
--data '{"link_access":"private","password":"","write_password":""}'Without the clears, a passworded object returns 409 link_access_conflict with password_protected and write_password_protected in the body, and nothing changes. Confirm with the person who set those passwords before removing them. The request also returns 400 link_access_conflict when it combines private with a new password or with write_policy: "org", or when it sets a password on an object that is already private, including X-Energon-Set-Password on PUT /v1/files/{id}. A 409 link_access_conflict can also mean another change to access or passwords landed during the update; read the object again and retry.
Going private stores write_policy: "owner". To make it public again, PATCH {"link_access":"public"}, optionally with a new password in the same request. Write policy stays owner until the creator changes it.
Copies, exports, cleanup, and grants
- Duplicates. A copy does not inherit link access, so a copy of a private object is public unless the duplicate request sends
link_access: "private". Only the creating account can copy a private object, because other accounts cannot find it. - Account export.
GET /v1/exportand Download what you own include private objects. Eachmanifest.jsonentry carrieslink_access. A private entry’surlstill answers403 link_private; read it through/v1. - Cleanup. In
POST /v1/cleanup, another account’s private id is skipped asnot_found, notnot_writable, so a cleanup preview does not confirm that the id exists. Admin cleanup sees private objects from every account. - Upload grants. A
new_filetarget with"link_access": "private"makes the file private from the moment it lands. Addingpasswordorwrite_passwordto that target is400 link_access_conflict. A grant for another account’s private file or site is404. See Let a machine upload without a token.
Private work and tool gateways
Because a private object is visible only to the creating account, a tool gateway whose key belongs to its own account cannot read or change a person’s private work, and anything private the gateway creates belongs to the gateway’s account. If the gateway should work with a person’s private objects, create its key from that person’s account.
Private is not a shared-with list. There is no way to add another account, and anyone holding one of your agent keys reads the object as you. If the creating account goes away, nobody can read the object; admins can still see its metadata and set its expiry or delete it through admin cleanup. Rolling this Energon’s Worker back to a release before v1.15.0 serves private objects publicly again; see Upgrade and recover an Energon.
For the exact fields and error codes, see HTTP API. For how link access sits next to share passwords, write passwords, and write policy, read Security model.