Skip to content

Public endpoint via Cloudflare Tunnel ​

TinyS3 can expose a running server through a Cloudflare Tunnel, giving it a public https://… URL without opening a firewall port. It is delivered as an optional plugin (tiny-s3-cloudflared) that drives a native libcloudflared library through the Java Project Panama (FFM) API.

PUBLIC EXPOSURE

Opening a tunnel makes the server reachable from the public internet. Enable TLS and configure explicit (non–auto-generated) root credentials before sharing the URL. The server logs a prominent warning at startup, and an extra warning when TLS is disabled and the root credentials were auto-generated.

Enabling ​

The tunnel is opt-in and only activates with an explicit, deliberately-named flag — it is never enabled implicitly (for example by an environment variable):

bash
java -jar tinys3-all.jar --config config.yaml --allow-cloudflared-public-endpoint

Or activate it from the YAML config under the experimental: section (it maps to the same flag):

yaml
experimental:
  cloudflared:
    enabled: true   # shorthand: `cloudflared: true`

It lives under experimental: — not plugins: — because opening a public tunnel is an effect-heavy, less-stable feature that should read as deliberate.

  • Quick tunnel (default): with no TUNNEL_TOKEN set, a random https://<name>.trycloudflare.com URL is minted and printed in the startup banner.
  • Named tunnel: set TUNNEL_TOKEN=<token> to attach a pre-configured Cloudflare tunnel. The token is read only after the flag has activated the plugin, and is used solely to choose named vs quick mode. It is never logged or persisted.

Native library resolution ​

The native libcloudflared library is resolved, in order, from:

  1. the JVM loader / java.library.path (System.loadLibrary semantics), which is seeded from LD_LIBRARY_PATH (Linux) / DYLD_LIBRARY_PATH (macOS) / PATH (Windows);
  2. a libcloudflared.<ext> sitting next to the tinys3 single-file binary — the launcher detects it and prepends its directory to that search path for you (the "drop it in and go" path, see below);
  3. on linux/x86-64 only, a gzip-compressed copy bundled in the jar — extracted to a private per-run directory and checksum-verified before load. So the tunnel works out of the box on the most common server platform with no download and no flags; every other platform uses 1–2.

For safety the loader never searches the working directory or walks parent directories. The launcher's sibling lookup (2) is scoped to the directory of the executable itself — operator-placed, not an ambient CWD search. To pin a specific library yourself, put its directory on LD_LIBRARY_PATH (or the platform equivalent). The shipping fat jars already declare Enable-Native-Access: ALL-UNNAMED in their manifest.

Download a prebuilt library (releases) ​

linux/x86-64 needs nothing — the library is bundled in the jar for that platform and loads automatically. For every other platform the library is published as a separate per-platform asset on each GitHub release (it is ~30 MB, so it is not bundled for all platforms). Download the one for your platform into the same folder as the tinys3 binary and name it libcloudflared.so (.dylib on macOS); the launcher picks it up automatically on the next start.

bash
# Linux x86-64 — next to the tinys3 binary:
curl -fSL -o libcloudflared.so \
  https://github.com/devdilson/tiny-s3/releases/download/v1.4.0/libcloudflared-linux-amd64.so

# Linux ARM64:
curl -fSL -o libcloudflared.so \
  https://github.com/devdilson/tiny-s3/releases/download/v1.4.0/libcloudflared-linux-arm64.so

# verify against the release checksums, then run as usual:
curl -fSL https://github.com/devdilson/tiny-s3/releases/download/v1.4.0/SHA256SUMS-cloudflared -o SHA256SUMS-cloudflared
sha256sum -c SHA256SUMS-cloudflared --ignore-missing
./tinys3 server /data --allow-cloudflared-public-endpoint

If you run the fat jar directly instead of the single-file binary, put the library's directory on the dynamic-library search path: LD_LIBRARY_PATH=$PWD java -jar tinys3-all.jar … (Linux; DYLD_LIBRARY_PATH on macOS, PATH on Windows).

Plugin allowlist ​

Server plugins are discovered via ServiceLoader. As a defense-in-depth trust boundary, an optional allowlist restricts which plugins may activate:

bash
# comma-separated plugin names; only these may activate when set
TINY_S3_PLUGINS=cloudflared java -jar tinys3-all.jar \
  --config config.yaml --allow-cloudflared-public-endpoint
# or: -Dtinys3.plugins=cloudflared

When unset (the default), all discovered plugins are eligible and gate themselves via their own enablement flag.

Building the native library ​

Requires the cloudflared git submodule and a Go toolchain (cgo):

bash
./gradlew :tiny-s3-cloudflared:buildCloudflaredSharedLib

The native build is skipped automatically when the submodule or Go toolchain is absent, so ./gradlew build still succeeds without them — only the tunnel runtime is unavailable.

Cross-building the release assets ​

The per-platform libraries published on releases are cross-compiled from one machine with zig as the cgo C toolchain (no Docker or per-target JDK needed):

bash
cd integrations/cloudflared/cloudflared
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
  CC="zig cc -target x86_64-linux-gnu"  CXX="zig c++ -target x86_64-linux-gnu" \
  go build -buildmode=c-shared -ldflags "-s -w" -o libcloudflared-linux-amd64.so ./cmd/libcloudflared
CGO_ENABLED=1 GOOS=linux GOARCH=arm64 \
  CC="zig cc -target aarch64-linux-gnu" CXX="zig c++ -target aarch64-linux-gnu" \
  go build -buildmode=c-shared -ldflags "-s -w" -o libcloudflared-linux-arm64.so ./cmd/libcloudflared

Regenerating the bundled library (libcloudflared.so.gz) ​

The linux/x86-64 library is committed gzip-compressed at integrations/cloudflared/src/main/resources/native/linux-x86-64/libcloudflared.so.gz (~9 MB) so the tunnel works from the jar with no download. The loader pins the SHA-256 of the decompressed library, so whenever you rebuild it you must update that pin in two places or the guard test fails. Prerequisites: the cloudflared submodule, Go, and zig.

bash
# 1. Build the stripped linux/x86-64 c-shared library (from the repo root):
cd integrations/cloudflared/cloudflared
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
  CC="zig cc -target x86_64-linux-gnu" CXX="zig c++ -target x86_64-linux-gnu" \
  go build -buildmode=c-shared -ldflags "-s -w" -o /tmp/libcloudflared.so ./cmd/libcloudflared
cd -

# 2. Compress it into the resource path:
gzip -9 -c /tmp/libcloudflared.so \
  > integrations/cloudflared/src/main/resources/native/linux-x86-64/libcloudflared.so.gz

# 3. Compute the SHA-256 of the *decompressed* library:
shasum -a 256 /tmp/libcloudflared.so

Paste that hash into both:

  • BUNDLED_SHA256 in NativeLibraryLoader.java, and
  • EXPECTED_SHA256 in BundledNativeLibraryTest.java.

Then verify — the guard test confirms the committed resource decompresses to the pinned hash:

bash
./gradlew :tiny-s3-cloudflared:test --tests '*BundledNativeLibraryTest*'

The pinned hash and the compressed resource must always move together; the test exists to catch a resource swap or a stale pin at build time rather than as a runtime checksum failure on a user's machine.

TinyS3 is AGPLv3 licensed. Cloudflare and Cloudflare Workers are trademarks of Cloudflare, Inc. Amazon S3 and AWS are trademarks of Amazon.com, Inc. or its affiliates. MinIO is a trademark of MinIO, Inc. They are named only to describe compatibility; totis.ai is not affiliated with, endorsed by, or sponsored by any of them.