Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions content/storage/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ title: 'Storage'
linkTitle: 'Storage'
weight: 4
icon: 'disk'
description: 'Persistent disks for workloads that need to keep data between restarts.'
lead: 'Containers are ephemeral. When a workload needs to persist data across restarts and rollouts, attach a managed disk.'
description: 'Persistent disks for workloads that keep data between restarts, plus temporary file sharing.'
lead: 'Containers are ephemeral. Attach a managed disk when a workload needs to persist data across restarts and rollouts, or use Dropbox to hand a file out as a short-lived download URL.'
---
130 changes: 130 additions & 0 deletions content/storage/dropbox.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
---
title: 'Dropbox'
linkTitle: 'Dropbox'
weight: 2
description: 'Temporary file storage — upload a file, get a short-lived public download URL.'
lead: 'Dropbox is temporary file storage for a project. Upload a file and you get back a public, signed download URL that expires on its own — a day by default, up to seven. Good for handing someone a build artifact, sharing a generated report, or staging an archive to publish — without standing up a bucket or minting long-lived credentials.'
---

## When to reach for it

A disk is for data a deployment keeps. Dropbox is the opposite: a file you want
to *get out* of the platform briefly and then forget about.

- **Share an artifact** — a build output, a database dump, a log bundle — with
someone who just needs a link, not an account.
- **Hand a generated file to a human** — an export or report produced by a job,
surfaced as a URL they can click.
- **Stage a static-site archive** — upload a `.tar.gz`, then point
[static-site publishing](/deployments/static-sites/) at the returned URL.

Every file carries a **TTL of 1–7 days** (default 1). Once it expires the link
stops working and the bytes are reclaimed — there's no manual delete and no way
to extend a TTL. If you need a file to live longer, re-upload it.

The download URL is signed: a tampered or made-up token is rejected before it
ever touches storage, so a link only works if it came from a real upload. But
the URL itself is the only credential — anyone who has it can download the file
until it expires. Treat a Dropbox link like a secret, and don't use Dropbox for
anything you need to keep private indefinitely.

## The Dropbox page

Open **Dropbox** in a project to upload files and see what's currently stored.

{{< shot src="/img/dropbox-list.png" url="console.deploys.app/dropbox?project=acme" alt="Dropbox: a drop zone above a list of four uploaded files, each with a download URL, size, and expiry" caption="Drop a file in, get a shareable URL. Each row shows the file's size, when it went up, and when it expires." >}}

Drag a file onto the drop zone (or click to browse) and hit **Upload**. The new
file appears in the list below with its download URL — copy it with the clipboard
button, or open it in a new tab. Each row shows the size, the upload time, and
when the link expires.

The **Usage** button (top right) opens charts for the project's Dropbox
**egress** (bytes downloaded) and **storage** (bytes held) over the last 7, 30,
or 90 days.

## Upload from the API

Uploads are a raw `POST` to the Dropbox service — not a JSON-RPC action — so they
live on their own host, `https://dropbox.deploys.app/`. The request carries your
normal Bearer token; the caller needs the `dropbox.upload` permission on the
project.

```bash
curl -fsS -X POST \
"https://dropbox.deploys.app/?project=acme&ttl=3&filename=build.tar.gz" \
-H "Authorization: Bearer $DEPLOYS_TOKEN" \
--data-binary @build.tar.gz
```

| Query param | | Description |
|---|---|---|
| `project` | required | Project ID the upload is authorized and billed against |
| `ttl` | optional | Lifetime in days, 1–7 (default 1) |
| `filename` | optional | Name recorded in `Content-Disposition` for the download |

The same values can be passed as `param-project` / `param-ttl` / `param-filename`
headers instead; query params win when both are present. A successful response is
JSON:

```json
{
"ok": true,
"result": {
"downloadUrl": "https://dropbox.deploys.app/files/<token>",
"expiresAt": "2026-06-19T08:00:00Z"
}
}
```

On an auth or validation failure the service still answers `200` but with
`{"ok": false, "error": {"message": "…"}}` — check the `ok` field, not just the
HTTP status.

### From Go

The [typed client](/api/conventions/#typed-clients) wraps the upload in a helper
that returns the URL, expiry, and byte count:

```go
res, err := c.DropboxUpload(ctx, &client.DropboxUploadOptions{
Project: "acme",
Content: content, // the file bytes
Filename: "build.tar.gz", // optional
TTLDays: 3, // 1–7, default 1
})
// res.DownloadURL, res.ExpiresAt, res.Size
```

This is exactly what static-site publishing uses internally: upload an archive,
then pass `res.DownloadURL` to `PublishSite`.

## List and meter from the CLI

The [`deploys` CLI](/automation/cli/) exposes the read side — listing stored
files and pulling usage metrics. (Uploading is done through the console or the
API above.) Both require the `dropbox.list` permission.

```bash
# everything currently stored in the project
deploys dropbox list --project acme

# narrow by upload time, cap the count
deploys dropbox list --project acme \
--after 2026-06-01 --before 2026-06-15 --limit 20

# egress + storage over the last 30 days (7d / 30d / 90d)
deploys dropbox metrics --project acme --time-range 30d
```

`list` returns each file's download URL, filename, size, TTL, and its created /
expires timestamps — the same view the console shows.

## Permissions

| Permission | Grants |
|---|---|
| `dropbox.upload` | Upload files to the project |
| `dropbox.list` | List stored files and read usage metrics |

Grant these through a [role](/access/roles/) like any other permission.
1 change: 1 addition & 0 deletions scripts/screenshots/capture.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const screens = [
['domain-list', `/domain?${P}`],
['route-list', `/route?${P}`],
['disk-list', `/disk?${P}`],
['dropbox-list', `/dropbox?${P}`],
['registry-list', `/registry?${P}`],
['role-list', `/role?${P}`],
['service-account-list', `/service-account?${P}`],
Expand Down
48 changes: 43 additions & 5 deletions scripts/screenshots/mock-enrichment.patch
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
diff --git a/src/lib/server/mock.js b/src/lib/server/mock.js
index 7849ada..56a4585 100644
index 1402806..c360791 100644
--- a/src/lib/server/mock.js
+++ b/src/lib/server/mock.js
@@ -262,58 +262,72 @@ const deployments = [
@@ -268,58 +268,72 @@ const deployments = [
}
]

Expand Down Expand Up @@ -120,7 +120,7 @@ index 7849ada..56a4585 100644
]

const wafZone = {
@@ -667,6 +681,20 @@ const roles = [
@@ -673,6 +687,20 @@ const roles = [
createdAt: CREATED_AT,
createdBy: USER_EMAIL
},
Expand All @@ -141,7 +141,7 @@ index 7849ada..56a4585 100644
{
role: 'admin',
name: 'Administrator',
@@ -690,7 +718,15 @@ const serviceAccounts = [
@@ -696,7 +724,15 @@ const serviceAccounts = [
sid: 'sa_mock_ci',
email: 'ci@acme.serviceaccount.deploys.app',
name: 'CI Deployer',
Expand All @@ -158,7 +158,7 @@ index 7849ada..56a4585 100644
createdAt: CREATED_AT,
createdBy: USER_EMAIL
}
@@ -787,7 +823,10 @@ const auditLogItems = (() => {
@@ -793,7 +829,10 @@ const auditLogItems = (() => {

const repositories = [
{ name: 'acme/web', size: 184320000, createdAt: CREATED_AT },
Expand All @@ -170,3 +170,41 @@ index 7849ada..56a4585 100644
]

const repositoryTags = [
@@ -978,12 +1017,36 @@ function invoiceListItem (inv) {

const dropboxItems = [
{
- downloadUrl: 'https://dropbox.deploys.app/d/mock-build.tar.gz',
+ downloadUrl: 'https://dropbox.deploys.app/files/k3Qp9mZx7Rt2Lv8Wd4Nc1Bg-a1b2c3d4e5f6a7b8c9d0',
filename: 'build.tar.gz',
size: 5242880,
ttl: 86400,
createdAt: CREATED_AT,
expiresAt: '2026-05-02T08:00:00Z'
+ },
+ {
+ downloadUrl: 'https://dropbox.deploys.app/files/Hn5Tb2Wq8Yx1Kd6Vc3Mf7Lp-9f8e7d6c5b4a3210ffee',
+ filename: 'db-dump-2026-05-01.sql.gz',
+ size: 50331648,
+ ttl: 604800,
+ createdAt: CREATED_AT,
+ expiresAt: '2026-05-08T08:00:00Z'
+ },
+ {
+ downloadUrl: 'https://dropbox.deploys.app/files/Rj4Cf8Nz1Px6Bm2Gd9Wt5Qk-1234abcd5678ef90aa11',
+ filename: 'report-april.pdf',
+ size: 1258291,
+ ttl: 259200,
+ createdAt: CREATED_AT,
+ expiresAt: '2026-05-04T08:00:00Z'
+ },
+ {
+ downloadUrl: 'https://dropbox.deploys.app/files/Wd7Lq2Vx9Bn4Kp1Cf6Mz3Rt-0fe1dc2b3a49587667cc',
+ filename: 'site.tar.gz',
+ size: 839680,
+ ttl: 172800,
+ createdAt: CREATED_AT,
+ expiresAt: '2026-05-03T08:00:00Z'
}
]

Binary file added static/img/dropbox-list-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/img/dropbox-list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.