Overview
Tornado writes every completed file into the OSS bucket you configure, then hands your customers a presigned GET URL for it (thes3_url / download_url field
in job status and in the job.completed webhook).
Both halves matter for the RAM policy. The worker needs to write, and the
presigned link needs the same identity to be allowed to read. A presigned URL
carries no permissions of its own: it is signed with your AccessKey pair, and OSS
evaluates the download as that identity. Grant write without read and the setup
looks healthy right up until a customer clicks a link.
The Settings screen shows an Endpoint (optional) field for OSS. On the
dashboard route that value is accepted, stored and echoed back masked, but
nothing reads it: the structural check ignores it, and the dashboard’s own
presigning path does not support OSS at all. The endpoint only becomes
load-bearing on the API route,
POST /user/oss,
where it is required and where the region is derived from it. Leave it empty in
Settings unless you are mirroring an API configuration.Before you start
You need:
- An Alibaba Cloud account with permission to create an OSS bucket, a RAM user, and a custom RAM policy.
- An OSS bucket dedicated to Tornado deliveries. Mixing it with unrelated data makes a scoped policy pointless.
- A RAM user with a permanent AccessKey pair. Tornado stores the pair and signs its own requests, so an STS session token is not usable here: there is no field for one.
- Admin or Owner role in your Tornado organisation.
POST /settings/storageis gated on that role; ordinary members can read the configuration but not change it.
1. Create the bucket and choose a region
1
Create the bucket
In the OSS console (
oss.console.aliyun.com), open Buckets and choose
Create Bucket. You set Bucket Name and Region in the same panel.2
Choose the region deliberately
A bucket’s region is the physical location of the data and cannot be changed
after creation. Getting it wrong means creating a second bucket and moving
the objects. Pick the region closest to whoever consumes the files: your
backend, your users, or your CDN origin.
3
Keep the bucket private
Leave the ACL at Private. Tornado never needs public objects. Delivery
works through presigned URLs, which is exactly what makes a private bucket
sufficient.
The region identifier
Two related strings are easy to confuse:- The region ID, for example
cn-hangzhou. - The endpoint host, which is the region ID with an
oss-prefix and the Alibaba suffix:oss-cn-hangzhou.aliyuncs.com.
oss- plus the region ID, which
is what the placeholder in the wizard shows:
2. Public, internal and accelerate endpoints
Every OSS region publishes several endpoint hosts for the same bucket. They are not interchangeable:3. Create a RAM user scoped to the bucket
Do not hand Tornado your root account AccessKey. Create a RAM user whose only permission is the delivery bucket.The policy: minimum
Exactly the four actions Tornado calls against your bucket, and nothing else. Replacemy-tornado-downloads with your bucket name and save it as a custom
policy.
Minimum delivery policy
Do not add
oss:InitiateMultipartUpload, oss:UploadPart or
oss:CompleteMultipartUpload. Above 50 MB uploads are multipart, and with
Tornado’s streaming upload path a file of any size may be, but all three of
those calls are authorised by oss:PutObject. AbortMultipartUpload is the
only step of the lifecycle with a permission of its own, which is exactly why
it is the one that gets left out.The policy: recommended
The minimum is the recommendation. OSS has nothing worth adding: every other action Tornado could plausibly need is either unused or already covered.
The one thing worth configuring is not a permission: set a lifecycle rule on
the bucket to expire incomplete multipart uploads. It catches the cases
Tornado cannot clean up itself, such as a worker that died mid-upload, and it
needs no RAM permission at all.
Creating the user and attaching the policy
1
Create the custom policy
In the RAM console, go to Permissions then Policies, and choose
Create Policy. Switch to the JSON editor, paste the policy above, and
give it a name such as
TornadoDelivery.2
Create the RAM user
Go to Identities then Users, and choose Create User. Give it a
logon name such as
tornado-delivery.For Access Mode, select programmatic access only (the option for using a
permanent AccessKey). Do not also grant console access. This is a machine
identity and never needs to sign in.3
Attach the policy
From the user’s row, use Attach Policy / Grant Permission and select
the
TornadoDelivery policy. Attach nothing else.4. Get the AccessKey pair
If you created the user with programmatic access, the AccessKey pair is generated during creation. Otherwise, open the user, go to its Credential tab, and in the AccessKey section choose Create AccessKey. You get an AccessKey ID (Alibaba’s RAM user keys beginLTAI) and an
AccessKey secret.
5. Fill in the fields
Tornado’s OSS configuration isbucket, region, access_key_id,
access_key_secret, plus an optional endpoint.
1
Onboarding wizard
On the Destination step, choose the Alibaba OSS tile. The
Credentials step shows four inputs, and the note “Use a RAM user scoped to
this bucket”, which is the policy from step 3.
The wizard has no endpoint field for this tile.
2
Or Settings, after onboarding
Dashboard Settings, section Default storage destination, provider
Alibaba Cloud OSS. The same fields appear, labelled Bucket,
Region, Access Key ID and Access Key Secret, plus
Endpoint (optional).Settings has a save action and no “Test connection” button, so nothing is
checked there at all. That is not much of a loss for OSS, given what the test
does and does not do.
Credentials submitted through the dashboard are written to Azure Key Vault, not
to the application database. Only the provider name and a vault reference are
stored alongside your organisation, and reading the configuration back returns
the secret masked. If Key Vault is unreachable, the save is refused with a 503
rather than falling back to plaintext storage.
Verifying for real
Since the dashboard test never leaves the building for OSS, verification is on you. There are two checks, and they answer different questions.Check 1: make Alibaba answer
POST /user/oss configures storage for one
API key and performs a live probe while doing it: it uploads
verify_credentials.txt to the bucket root, then deletes it (best effort). A
200 means Alibaba accepted a real authenticated write. A 400 means it did not,
and the response carries the reason.
Note the different contract on this route: it takes a full endpoint URL and no
region.
Check 2: prove the read half
The probe in check 1 writes and deletes. It never reads, so it cannot detect a missingoss:GetObject, and it never opens a multipart upload, so it cannot
detect a missing oss:AbortMultipartUpload either. The dashboard’s S3 probe was
extended to cover both; the OSS path was not, so this check is still on you.
Run one real job, take the s3_url from the job status or the job.completed
webhook, and fetch a single byte:
videos/ by default, with any folder_prefix nested inside that. See
Custom Storage for the full path layout.
Fixing the policy repairs existing links. A presigned URL is evaluated at
request time against the current policy, so a link issued while
oss:GetObject
was missing starts working the moment you grant it, as long as it has not
expired.Troubleshooting
Because the dashboard test is structural, the only error strings it can produce are about the form itself:
Everything else surfaces from
POST /user/oss or from a real job. Those errors are
prefixed Invalid configuration: for local validation and
Credential validation failed: Upload test failed: for anything Alibaba returned.
Edge cases
Large files and multipart uploads
Large files and multipart uploads
Uploads above roughly 50 MB are sent as multipart, which is most videos.
Tornado also streams straight from the muxer into OSS for these, so parts are
uploaded concurrently while the file is still being produced.None of that needs extra permissions on the happy path: Alibaba documents
oss:PutObject as the action required to initiate a multipart upload, upload
parts, and complete it. Only the abort path is separate, which is why
oss:AbortMultipartUpload is in the policy.Where files land inside the bucket
Where files land inside the bucket
By default objects go under a
videos/ top-level folder, with any
folder_prefix nested inside it and the per-job folder below that. Both
base_folder and folder_prefix are configurable, though only on the API
route: the dashboard OSS form does not expose either. See
Custom Storage for the full path layout.Rotating the AccessKey pair
Rotating the AccessKey pair
A RAM user can hold two AccessKey pairs at once, which is the whole rotation
mechanism.Presigned URLs signed with the old pair stop working the moment that pair is
disabled or deleted, even if the link has not expired. If your customers hold
live links, disable rather than delete, and wait the window out.
1
Create the second pair
On the same RAM user, create a second AccessKey.
2
Update Tornado
Save the new pair. In-flight jobs finish with whichever credentials they
already resolved.
3
Watch, then delete
Confirm new jobs deliver and new links download, then disable the old
AccessKey. Disabling first is the reversible version of deleting.
Removing the configuration
Removing the configuration
Deleting the dashboard configuration removes the destination for your
organisation and the backing Key Vault secret. For a per-key configuration set
through the API, use
DELETE /user/oss with that key. After
removal, new downloads go to Tornado’s managed storage. Files already in your
bucket are untouched.Related
Custom Storage
All providers, the per-API-key configuration route, folder layout, and inline per-request credentials
Webhooks
The
job.completed payload, including the presigned URL this policy has to allow