S3¶
Roto stores S3 bucket and object metadata in SQLite and object bodies as files. The AWS API, inspection UI, and browser view all read the same objects. Upload through an SDK, the AWS CLI, or Lua; the browser view immediately reflects the latest object versions. No export, separate site build, or website configuration is needed to browse a bucket.
Start and connect¶
Start a disposable server with populated example buckets:
Open the inspection UI at http://localhost:5071/roto-api/, choose S3, then
use Open in browser on a bucket or object. For a directory listing, try
http://localhost:5071/s3/demo-assets/.
For an empty disposable server:
Use dummy credentials for local API requests. SDK clients should use the Roto endpoint and path-style S3 addressing:
import boto3
from botocore.config import Config
s3 = boto3.client(
"s3",
endpoint_url="http://localhost:5071",
region_name="us-east-1",
aws_access_key_id="test",
aws_secret_access_key="test",
config=Config(s3={"addressing_style": "path"}),
)
s3.create_bucket(Bucket="my-bucket")
s3.put_object(
Bucket="my-bucket",
Key="foo.html",
Body=b'<h1>Hello</h1><a href="../about.html">About</a>',
ContentType="text/html; charset=utf-8",
)
s3.put_object(
Bucket="my-bucket",
Key="about.html",
Body=b'<h1>About</h1><a href="../foo.html">Home</a>',
ContentType="text/html; charset=utf-8",
)
Open http://localhost:5071/s3/my-bucket/foo.html. The HTML renders directly,
and clicking About loads the other S3 object.
AWS API coverage¶
S3 uses the AWS REST-XML protocol. The main implemented API groups are:
| Area | Operations and behavior |
|---|---|
| Buckets | Create, list, head, delete, and get location |
| Objects | Put, get, head, copy, delete, batch delete, object attributes, metadata, range and conditional reads |
| Listings | ListObjects and ListObjectsV2 with prefixes, delimiters, and pagination |
| Versioning | Get/put versioning, list versions, explicit version reads and deletes, delete markers |
| Multipart | Create upload, upload/copy parts, list uploads/parts, complete, abort |
| Tags and ACLs | Bucket/object tagging and ACL read/write; ACLs are stored without permission enforcement |
| Configuration | Website, CORS, lifecycle, encryption, replication, ownership controls, public access block, logging, notifications, acceleration, request payment, and policies |
Configuration storage does not imply an execution engine. Lifecycle and
replication settings are stored rather than running expiration or replication
workers. Encryption configuration does not provide production cryptographic
protection. Policies, public access settings, and ACLs are not a security boundary.
Object lock, restore, select, detailed checksum behavior, and SNS/SQS notification
delivery are outside the current core coverage. Unsupported modeled operations
return NotImplemented rather than silently pretending to execute them.
The API also supports virtual-host addressing, for example
my-bucket.s3.localhost:5071. This is an API hostname; use
my-bucket.s3-website.localhost:5071 for browser index resolution and listings.
Versions and multipart uploads¶
Enable versioning through PutBucketVersioning. Replacing an object creates a
new version, and deleting its current key creates a delete marker. Read or delete
an older version by passing VersionId to the SDK operation. The inspection UI
shows version IDs and latest/delete-marker flags; browser access uses the current
version only.
Multipart uploads use the normal S3 sequence: create the upload, upload numbered parts, then complete with their part numbers and ETags. Incomplete uploads are visible through upload and part APIs and the inspection UI; they do not become browser objects until completed. Abort an upload to discard its pending parts.
Object notifications¶
Configure Lambda destinations using PutBucketNotificationConfiguration.
Supported events include object creation through Put, Copy, and multipart
completion, and removal through Delete/DeleteObjects, including delete markers.
Prefix/suffix filters and event wildcards are applied. EventBridge integration
also supports object events; see EventBridge.
Mutations persist notifications before asynchronous handoff. Persistent-mode
queues survive restarts; delivery is at least once, so consumers should tolerate
duplicates. Lambda handoff failures receive three attempts and remain visible at
/roto-api/s3/notifications. Browser reads and directory listings do not trigger
object mutation events. See Lambda S3 events
for executor setup, delivery details, and inspection examples.
Three ways to read objects¶
| Interface | Example | Behavior |
|---|---|---|
| AWS S3 API | Signed GET /my-bucket/foo.html |
Normal S3 object response for SDKs and CLI clients |
| Browser | GET /s3/my-bucket/foo.html |
Inline rendering, directory browsing, and index resolution |
| Inspection download | /roto-api/s3/object?bucket=my-bucket&key=foo.html |
Attachment download; optional version selection |
The browser paths do not replace the AWS API. Signed requests continue through
normal API routing. Browser requests
are unsigned, read-only GET or HEAD; other methods return 405. Write, copy,
delete, list versions, and change bucket settings through the AWS API.
Browser reads select the latest object version. A latest delete marker hides the object. Use the inspection UI or S3 version APIs for older versions; a browser query string is not an S3 API operation selector or version selector.
Browser paths and directories¶
The basic URL is:
It works with localhost, a DNS name, or an IP address. Object keys retain their
S3 meaning: / separates apparent folders but does not create filesystem
directories. Spaces, +, Unicode, ?, and # in keys must be URL encoded.
Generated directory links encode those characters automatically.
Resolution follows these rules:
- A non-directory path first attempts to read the exact object key.
- A directory path attempts its index document, defaulting to
index.html. - If there is no index but the prefix contains objects or child prefixes, Roto displays a directory listing. Empty buckets also have a root listing.
- A directory requested without its trailing
/redirects to the slash form, so relative links resolve correctly. - A missing object or directory returns 404, using a configured error document when available. Missing buckets return 404 as well.
For example, /s3/my-bucket/docs/ renders docs/index.html if it exists;
otherwise it lists immediate children under docs/. A stored exact object named
docs takes precedence when requesting /s3/my-bucket/docs.
Listings contain parent-directory navigation and separate folder and object links. Large listings have a Next page link using S3 continuation tokens; they are not limited to the first page of objects. An index page takes precedence over listing its directory.
HTML, styles, scripts, and other content¶
HTML is rendered inline. CSS, JavaScript, images, and other assets are ordinary
objects loaded by the browser. Roto preserves stored content types, content
encoding, caching metadata, and ETags, and removes attachment disposition on
browser reads. Range requests and ETag conditions are passed to S3 object reads.
HEAD returns headers without a response body.
Set ContentType explicitly when uploading, especially for uncommon formats.
For objects whose type is absent or generic (application/octet-stream or
binary/octet-stream), the browser view infers common types from extensions:
HTML, CSS, JavaScript, JSON, SVG, PNG, JPEG, GIF, WebP, ICO, text, PDF, WebAssembly,
and WOFF/WOFF2. Other types keep their stored response type.
No HTML rewriting or JavaScript rewriting occurs. Links work according to normal browser URL resolution:
Link in /s3/my-bucket/docs/index.html |
Resolved URL |
|---|---|
about.html |
/s3/my-bucket/docs/about.html |
../assets/style.css |
/s3/my-bucket/assets/style.css |
/s3/my-bucket/assets/style.css |
/s3/my-bucket/assets/style.css |
/assets/style.css |
/assets/style.css, outside the bucket mount |
For path-based browsing, use relative asset links or configure your site's build
base as /s3/my-bucket/. This applies to JavaScript imports, fetch URLs, CSS
url(...), forms, and client-side routers too. Roto does not automatically
fallback every missing URL to an SPA index page.
Bucket website hostnames¶
For sites designed to run at an origin's root, use:
Roto recognizes <bucket>.s3-website.<domain> in the HTTP Host header. The entire
hostname serves that bucket, including paths such as /roto-api/health, which
are object keys on a website host rather than inspection endpoints. /assets/
links therefore refer to objects under the bucket's assets/ prefix.
Many browsers resolve *.localhost to loopback. If your client does not, add
local DNS or hosts-file entries. For remote use, configure DNS (for example,
*.s3-website.dev.example.com) to point to the server. Bind the server to an
accessible interface:
An IP address alone works immediately with /s3/my-bucket/. It cannot select
multiple buckets by hostname; choose path access, DNS names, or a reverse proxy
that assigns a bucket hostname. A reverse proxy can also terminate HTTPS; retain
the website Host header when forwarding requests to Roto. Generated local links
and directory redirects use paths and work behind HTTPS proxies.
Website configuration and redirects¶
Bucket website configuration remains available through PutBucketWebsite,
GetBucketWebsite, and DeleteBucketWebsite. It applies to both browser paths
and website hostnames. For example:
s3.put_bucket_website(
Bucket="my-bucket",
WebsiteConfiguration={
"IndexDocument": {"Suffix": "home.html"},
"ErrorDocument": {"Key": "error.html"},
},
)
The configured index suffix replaces index.html in each directory. Missing
objects use the configured error object with HTTP status 404. If the error object
is unavailable, Roto returns a basic 404 page. Deleting website configuration
restores the default index and automatic listings; it does not disable browsing.
RedirectAllRequestsTo redirects to the configured host and optional protocol.
Website routing rules match key prefixes and/or the resulting HTTP error status,
then apply key replacement, host/protocol overrides, and a redirect status code.
Rules are evaluated in order; the first match wins. These browser conveniences
are not a complete reproduction of all AWS website endpoint edge cases.
Objects uploaded with WebsiteRedirectLocation redirect instead of displaying
content. In path mode, a local destination such as /about.html is mounted under
/s3/my-bucket/; absolute external URLs are preserved. Ordinary links inside
HTML are not adjusted this way. Object and whole-bucket redirects default to 302;
routing-rule redirects default to 301 unless configured otherwise.
Inspection, storage, and access boundaries¶
The inspection UI shows buckets, object versions, bucket configuration, and multipart resources. Open in browser opens the current object or bucket; Download retains attachment behavior, and Preview as text reads a bounded text preview. Opening an old version's row in the browser still selects the current version; downloading the row selects its explicit version.
Roto is a development simulator, not an authenticated static hosting service. Browser access does not enforce production S3 IAM or public-access policies. Path-based HTML shares the server's origin with the inspection UI, so only open content you trust. Website hostnames provide a separate browser origin from the inspection UI. Inspection downloads continue to force attachments rather than executing HTML on their own.
Persistent servers retain bucket metadata in the S3 database and bodies in the S3 blob store. Ephemeral servers discard state on exit. Browser reads never create objects or folders. See Storage and Services for the persistence model and broader S3 API support and limitations.
Verification¶
Build and run the browser integration checks using only Python's standard library; no Moto or Python packages are needed:
The regular Rust CI job runs the browser integration check on every push and pull request, in addition to the Cargo tests. It can also be run locally with the commands above.
The browser check starts and stops its own fresh ephemeral server using the reusable demo seed. It verifies rendered HTML and asset types, directory/index resolution, encoded keys, website hosts, error documents, redirects, conditional and range reads, inspection downloads, API separation, and deleted objects.