Skip to main content

Overview

Tornado writes every completed file into the OSS bucket you configure, then hands your customers a presigned GET URL for it (the s3_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.
“Test connection” does not contact Alibaba at all for OSS.For S3 the dashboard performs a real five-step round trip: it writes a probe object, reads it back, opens and aborts a multipart upload, deletes the probe, and reports the measured latency. For Azure Blob it writes and deletes. For OSS it does none of that. The check is structural only. It verifies that bucket, region, access_key_id and access_key_secret are non-empty strings and returns:
verified: false is the honest part of that response. A green result proves the form is filled in. It does not prove the credentials are valid, that the bucket exists, that the region is right, or that the RAM policy grants anything. No packet leaves Tornado.See Verifying for real before you rely on this destination.
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/storage is gated on that role; ordinary members can read the configuration but not change it.
Alibaba redesigns its console periodically. The navigation below names destinations rather than pixel positions. If a label has moved, the page you want is still the one that owns that object (bucket, user, policy).

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.
Tornado’s Region field wants the middle form, oss- plus the region ID, which is what the placeholder in the wizard shows:
Do not work from this table alone, and do not guess from a city name. Open the bucket in the OSS console and read its Overview page: the Port section lists the bucket’s own endpoints, public and internal. Copy the region segment from there. Alibaba operates many more regions than the eight above.

2. Public, internal and accelerate endpoints

Every OSS region publishes several endpoint hosts for the same bucket. They are not interchangeable:
Use the public endpoint. Tornado’s workers are not inside your VPC, so an internal endpoint is unreachable for them: the connection simply hangs until it times out. An internal host is easy to copy by accident, because the console lists it directly beneath the public one, and the two differ by the single word -internal.The accelerate hosts carry no region in the name. Tornado derives the signing region from the endpoint hostname, so an accelerate host does not yield a usable region and is not a supported form on this route. Stay on the regional public endpoint.

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. Replace my-tornado-downloads with your bucket name and save it as a custom policy.
Minimum delivery policy
Why each action is there, and nothing more:
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 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.
Scope the resource to the whole bucket, not to videos/. Delivered files land under videos/, but the credential probe writes verify_credentials.txt at the bucket root. A policy narrowed to my-tornado-downloads/videos/* fails credential validation with 403 AccessDenied even though real deliveries would have worked.

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 begin LTAI) and an AccessKey secret.
The AccessKey secret is displayed once, at creation, and Alibaba cannot show it again. Copy both values now. If you lose the secret, delete the AccessKey and create a new one.A RAM user may hold at most two AccessKey pairs. That limit is the rotation mechanism: create the second, update Tornado, confirm deliveries and downloads still work, then delete the first.

5. Fill in the fields

Tornado’s OSS configuration is bucket, 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.
Storage on this route is configured per API key, not per account. A configuration set on one key does not apply to your other keys, and a job sent with an unconfigured key lands in Tornado’s managed storage while still reporting Completed. This is the single most common cause of “my file isn’t in my bucket”. Use the key you actually send jobs with. See Custom Storage for the full rule.

Check 2: prove the read half

The probe in check 1 writes and deletes. It never reads, so it cannot detect a missing oss: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:
Then open the bucket in the OSS console and confirm the object is where you expect it: under 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

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.
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.
A RAM user can hold two AccessKey pairs at once, which is the whole rotation mechanism.
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.
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.
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.

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