···11+[package]
22+name = "porxie"
33+description = "A correct and efficient ATProto Blob proxy service with caching and moderation takedowns."
44+authors = ["Blooym"]
55+version = "0.1.0"
66+edition = "2024"
77+88+[dependencies]
99+axum = { version = "0.8.8", features = ["http2"] }
1010+anyhow = "1.0.102"
1111+clap = { version = "4.5.60", features = ["derive", "env"] }
1212+dotenvy = "0.15.7"
1313+serde = { version = "1.0.228", features = ["derive"] }
1414+tokio = { version = "1.49.0", features = [
1515+ "macros",
1616+ "rt-multi-thread",
1717+ "signal",
1818+ "net",
1919+] }
2020+tower-http = { version = "0.6.8", features = [
2121+ "catch-panic",
2222+ "normalize-path",
2323+ "trace",
2424+ "timeout",
2525+] }
2626+tracing = "0.1.44"
2727+tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
2828+reqwest = { version = "0.12.27", default-features = false, features = [
2929+ "http2",
3030+ "charset",
3131+ "system-proxy",
3232+ "stream",
3333+ "rustls-tls",
3434+] }
3535+futures = "0.3.32"
3636+infer = "0.19.0"
3737+cid = "0.11.1"
3838+multihash-codetable = { version = "0.1.4", features = ["sha2", "blake3"] }
3939+jacquard-common = "0.9.5"
4040+jacquard-identity = "0.9.5"
4141+bytesize = { version = "2.3.1", features = ["serde"] }
4242+mime = "0.3.17"
4343+humantime = "2.3.0"
4444+moka = { version = "0.12.13", features = ["future", "logging"] }
4545+axum-extra = { version = "0.12.5", features = ["typed-header"] }
+153
README.md
···11+# Porxie
22+33+A correct and efficient ATProto Blob proxy service with caching and moderation takedowns.
44+55+## Features
66+77+- **Secure by default** - verifies blob CIDs are legitimate and serves them with strict headers.
88+- **Primitive mimetype filter** - auto-detects blob MIME type from content and optionally restricts which mimetypes can be served. (Note: this validation is basic and falls back to `application/octet-stream` if enabled).
99+- **In-memory cache** - TinyLFU-based caching for fast repeat access to frequently requested content and moderation actions.
1010+- **Moderation service** - optional integration with an external custom moderation service to provide content takedowns. Bring your own policies.
1111+- **Manual cache purging** - Cached content and moderation status can be purged via a simple authenticated HTTP DELETE.
1212+1313+## Routes
1414+1515+- **GET** `/did/cid` - Resolve and fetch a blob from its origin.
1616+- **DELETE** `/did/cid` - Invalidate blob and moderation cache for a specific blob. Requires configured bearer auth token.
1717+1818+## Setup
1919+2020+### Configuration
2121+2222+All options can be set via flags, environment variables, or a `.env` file. For up-to-date and complete help, please use the `--help` flag (which is what the help below is generated by).
2323+2424+```
2525+Usage: porxie [OPTIONS]
2626+2727+Options:
2828+ --address <ADDRESS>
2929+ Socket address to bind the server to
3030+3131+ [env: PORXIE_ADDRESS=]
3232+ [default: 127.0.0.1:6314]
3333+3434+ --timeout <TIMEOUT>
3535+ Maximum duration before incoming requests are timed out
3636+3737+ [env: PORXIE_TIMEOUT=]
3838+ [default: 60s]
3939+4040+ --auth-token <AUTH_TOKEN>
4141+ Bearer token required to authenticate admin requests.
4242+4343+ When unset, all authenticated endpoints are unusable.
4444+4545+ [env: PORXIE_AUTH_TOKEN=]
4646+4747+ --allowed-mimetypes <ALLOWED_MIMETYPES>
4848+ List of mimetypes that can be served through this CDN.
4949+5050+ Validation is done loosely via content inference and is not foolproof. It is recommended to apply a sandboxed layer that will process the blob further to validate its type.
5151+5252+ [env: PORXIE_ALLOWED_MIMETYPES=]
5353+ [default: */*]
5454+5555+ --cache-header-value <CACHE_CONTROL_HEADER_VALUE>
5656+ The cache-control header value to send alongside responses.
5757+5858+ This header does not modify the internal cache lifetime of content, only what how it wants other clients to cache responses.
5959+6060+ [env: PORXIE_CACHE_HEADER_VALUE=]
6161+ [default: "public, max-age=604800, must-revalidate"]
6262+6363+ --cache-size <CACHE_SIZE>
6464+ Maximum size of cached responses in memory.
6565+6666+ Content is evicted using a TinyLFU policy that automatically prioritises the most frequently requested keys.
6767+6868+ It is recommended you deploy a dedicated caching service in front of this service for best cache performance. The built-in cache is optimised for handling frequent requests and bursts requesting the same content.
6969+7070+ The default value is conservatively low, you may wish to raise it to fit your needs.
7171+7272+ [env: PORXIE_CACHE_SIZE=]
7373+ [default: 512mb]
7474+7575+ --max-blob-size <MAX_BLOB_SIZE>
7676+ Maximum blob size that can be served through this CDN.
7777+7878+ Content that exceeds this limit will return an HTTP 413 error.
7979+8080+ [env: PORXIE_MAX_BLOB_SIZE=]
8181+ [default: 50mb]
8282+8383+ --moderation-cache-size <MODERATION_CACHE_SIZE>
8484+ Maximum size of cached moderation responses in memory.
8585+8686+ Each entry is lightweight, so small allocations can hold a large number of entries.
8787+8888+ [env: PORXIE_MODERATION_CACHE_SIZE=]
8989+ [default: 128mb]
9090+9191+ --moderation-cache-ttl <MODERATION_CACHE_TTL>
9292+ How long moderation responses are cached before being re-checked
9393+9494+ [env: PORXIE_MODERATION_CACHE_TTL=]
9595+ [default: 1h]
9696+9797+ --moderation-service-auth-token <MODERATION_SERVICE_AUTH_TOKEN>
9898+ Bearer auth token sent with all requests to the moderation service
9999+100100+ [env: PORXIE_MODERATION_SERVICE_AUTH_TOKEN=]
101101+102102+ --moderation-service-fail-open <MODERATION_SERVICE_FAIL_OPEN>
103103+ Whether to allow requests to proceed if the moderation service is unavailable or returns an unexpected status code
104104+105105+ [env: PORXIE_MODERATION_SERVICE_FAIL_OPEN=]
106106+ [default: false]
107107+ [possible values: true, false]
108108+109109+ --moderation-service-url <MODERATION_SERVICE_URL>
110110+ URL of an upstream moderation service that DID+CID pairs will be checked against.
111111+112112+ Requests are sent as HTTP GET <url>/<did>/<cid>.
113113+114114+ The service is expected to return HTTP 200 if permitted or HTTP 410 if taken down.
115115+116116+ [env: PORXIE_MODERATION_SERVICE_URL=]
117117+118118+ --plc-directory-url <PLC_DIRECTORY_URL>
119119+ URL of the PLC directory instance used for `did:plc` lookups.
120120+121121+ Can typically be left as default unless using a custom or test directory.
122122+123123+ [env: PORXIE_PLC_DIRECTORY_URL=]
124124+ [default: https://plc.directory]
125125+126126+ --upstream-https-only <UPSTREAM_HTTPS_ONLY>
127127+ Only allow HTTPS when connecting to upstreams.
128128+129129+ Disabling this is strongly discouraged outside of local development.
130130+131131+ [env: PORXIE_UPSTREAM_HTTPS_ONLY=]
132132+ [default: true]
133133+ [possible values: true, false]
134134+135135+ --upstream-proxy <UPSTREAM_PROXY>
136136+ HTTP(S) proxy for upstream requests. Supports embedded credentials (https://user:pass@host).
137137+138138+ When unset, the system proxy configuration is used automatically.
139139+140140+ [env: PORXIE_UPSTREAM_PROXY=]
141141+142142+ --upstream-timeout <UPSTREAM_TIMEOUT>
143143+ Maximum duration before upstream PDS requests are timed out
144144+145145+ [env: PORXIE_UPSTREAM_TIMEOUT=]
146146+ [default: 30s]
147147+148148+ -h, --help
149149+ Print help (see a summary with '-h')
150150+151151+ -V, --version
152152+ Print version
153153+```