Upload from a script or an agent
Your build makes images, or your coding agent makes a screenshot for a pull request. You want them on the forge without a browser.
The src CLI, the REST API and the MCP server can do everything that the Assets tab does. They use a token, so they act as you, or as the agent session that holds the token.
With src
You need src, logged in. Run the commands in a clone of the repository, or add -R OWNER/REPO.
src asset push dist/hero.png --to images # uploads images/hero.png
src asset push logo.svg favicon.ico # uploads to the top level
src asset list images/
src asset versions images/hero.png
src asset restore images/hero.png 2
pushuploads each file under its own name, in the folder of--to. A file with the same bytes as the current version printsUnchangedand adds no version.listandversionstake--json. TheurlandpinnedUrlfields give the two URLs of each asset.restoremakes a version current again.
To hide, delete, or change who can see the assets:
src asset hide images/draft.png # --unhide shows it again
src asset delete images/old.png --version 1 --yes
src asset visibility public # admins, private repositories only
src asset delete needs --yes, because src never asks a question. Without --version, it deletes the asset with all its versions.
Pull the assets into a folder
Your site can link to the URLs, so most builds need no copy. If your build must ship the files itself, pull them:
src asset pull --dir public
- It writes each asset to
public/<path>. Give a prefix, such assrc asset pull images/ --dir public, to pull only one folder. - It skips a file that has the same bytes already, and writes over a file with other bytes.
- It never writes outside
--dir, and never into a git folder. It names each file that it did not write, writes the others, then exits 1.
With REST
The routes are under /api/v1/repos/{owner}/{repo}, and they take a token in the Authorization header. Percent-encode each segment of the path: a space is %20, and # is %23.
Upload a file. The body is the file. X-Content-SHA256 is its SHA-256, and curl sends the Content-Length:
export TSS_FORGE_URL=https://forge.example/api/v1
export TSS_TOKEN=<your personal access token>
FILE=dist/hero.png
SHA=$(sha256sum "$FILE" | cut -d ' ' -f 1)
curl -X PUT "$TSS_FORGE_URL/repos/acme/site/assets/files/images/hero.png" \
-H "Authorization: Bearer $TSS_TOKEN" \
-H "Content-Type: application/octet-stream" \
-H "X-Content-SHA256: $SHA" \
--data-binary "@$FILE"
The answer is 201 for a new version, and 200 when the bytes are the current version already. Its JSON has version, created, url and pinnedUrl. A body that does not match the SHA-256 gets 422.
Read:
| Route | Does |
|---|---|
GET /assets?prefix=&cursor= | Lists one page of assets as { assets, next }. Send next back as cursor for the next page. |
GET /assets/versions/{path} | Lists the versions of an asset, newest first, with currentVersion and hidden. |
Change:
| Route | Does |
|---|---|
PUT /assets/files/{path} | Uploads a new version and makes it current. |
POST /assets/current/{path} with {"version": 2} | Makes a version current. |
POST /assets/hidden/{path} with {"hidden": true} | Hides the asset. false shows it again. |
DELETE /assets/files/{path} | Deletes the asset with all its versions. Add ?v=<n> to delete one version. |
PUT /assets/visibility with {"public": true} | Admins: makes the assets of a private repository public, or private with false. |
With MCP
The MCP server at /mcp has tools for the same features as REST. The tools take the same fields as the routes, with path as a field.
- Read:
list_assets,asset_versions. - Upload:
upload_asset. Thecontentfield is the file as base64, at most 5 MiB. Upload a larger file withsrcor REST. - Change:
set_asset_current,set_asset_hidden,delete_asset_version,delete_asset. - Admins:
set_assets_public.
Who can change assets
- People with write access, and their tokens.
- Agent sessions act as their person, and never with more access. A
fixersession can change assets. Areviewerorinvestigatorsession can only read them. - CI jobs can read assets, but cannot change them.
Limits
- A file is at most 100 MiB.
- The assets of one repository hold at most 10 GiB. The same bytes count once. Old versions count until you delete them.
- An upload over MCP is at most 5 MiB.
Your build uploads its files, and your pages link to their URLs. Versions and caching shows how to roll back when an upload goes wrong.