### Install Posthorn from Source Source: https://posthorn.dev/deployment/binary Install the Posthorn binary using Go modules. Requires Go 1.25+. ```bash go install github.com/craigmccaskill/posthorn/cmd/posthorn@latest ``` -------------------------------- ### Install Posthorn from Release Artifacts Source: https://posthorn.dev/deployment/binary Download, extract, and install the pre-built binary from GitHub releases. ```bash curl -L -o posthorn.tar.gz \ https://github.com/craigmccaskill/posthorn/releases/download/v1.0.0/posthorn-linux-amd64.tar.gz tar -xzf posthorn.tar.gz sudo install -m 0755 posthorn /usr/local/bin/posthorn ``` -------------------------------- ### Serve Command with Config Source: https://posthorn.dev/reference/cli Starts the posthorn listener using a specified configuration file. ```bash posthorn serve --config /etc/posthorn/config.toml ``` -------------------------------- ### Verify Posthorn Installation with Standalone Binary Source: https://posthorn.dev/getting-started/installation Run this command to verify the Posthorn installation when using the standalone binary. It checks the version of the installed tool. ```bash posthorn version ``` -------------------------------- ### Curl Examples for HTTP POST Requests Source: https://posthorn.dev/reference/http-api Demonstrates how to make POST requests to the API using curl. Includes examples for standard form data, multipart form data, and forcing a JSON response by setting the `Accept` header. ```bash # Success curl -X POST http://localhost:8080/api/contact \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "name=Test User&email=test@example.com&message=hello" ``` ```bash # Multipart curl -X POST http://localhost:8080/api/contact \ -F name="Test User" -F email=test@example.com -F message=hello ``` ```bash # Force JSON response curl -X POST http://localhost:8080/api/contact \ -H "Accept: application/json" \ -d "name=...&..." ``` -------------------------------- ### Start and Enable Posthorn Service Source: https://posthorn.dev/deployment/binary Manage the Posthorn systemd service, including starting, enabling, and checking status. ```bash sudo systemctl daemon-reload sudo systemctl enable --now posthorn sudo systemctl status posthorn ``` -------------------------------- ### Template Rendering Example Source: https://posthorn.dev/features/api-mode Demonstrates how keys from a flat JSON body are rendered as template variables. ```go-template Name: {{.name}} → Alice Count: {{.count}} → 42 Confirmed: {{.confirmed}} → true Tags: {{.tags}} → [urgent support] ``` -------------------------------- ### Run Posthorn CLI Commands Source: https://posthorn.dev/deployment/binary Examples of using the 'validate' and 'serve' CLI commands. ```bash posthorn validate --config /etc/posthorn/config.toml posthorn serve --config /etc/posthorn/config.toml ``` -------------------------------- ### Successful Posthorn Startup Logs Source: https://posthorn.dev/reference/cli Example of structured JSON logs indicating a successful posthorn service startup. ```json {"time":"2026-05-16T19:00:00Z","level":"INFO","msg":"posthorn starting","version":"v1.0.0","listen":":8080","config":"/etc/posthorn/config.toml","endpoints":2} {"time":"2026-05-16T19:00:00Z","level":"INFO","msg":"endpoint registered","path":"/api/contact","transport":"postmark","recipients":1} {"time":"2026-05-16T19:00:00Z","level":"INFO","msg":"http ingress listening","addr":":8080"} ``` -------------------------------- ### Verify Posthorn Installation Source: https://posthorn.dev/deployment/binary Check the installed Posthorn version. ```bash posthorn --version # posthorn v1.0.0 ``` -------------------------------- ### API Mode Request Example Source: https://posthorn.dev/reference/http-api A minimal example demonstrating a POST request to the API endpoint with Bearer authentication, JSON body, and idempotency key. ```http POST /api/transactional HTTP/1.1 Authorization: Bearer abc123 Content-Type: application/json Idempotency-Key: reset:user-42:2026-05-16T20 { "to_override": "alice@example.com", "subject_line": "Reset your password", "message": "https://app.example.com/reset/xyz" } ``` -------------------------------- ### Verify Posthorn Installation with Docker Source: https://posthorn.dev/getting-started/installation Run this command to verify the Posthorn installation when using Docker. It checks the version of the running container. ```bash docker run --rm ghcr.io/craigmccaskill/posthorn:latest version # posthorn v1.0.0 ``` -------------------------------- ### PostHog Process Start Event Source: https://posthorn.dev/reference/log-format Emitted once when the PostHog process starts. Includes version, listening address, configuration path, and number of endpoints. ```json { "level": "INFO", "msg": "posthorn starting", "version": "v1.0.0", "listen": ":8080", "config": "/etc/posthorn/config.toml", "endpoints": 2 } ``` -------------------------------- ### Run Posthorn Commands Source: https://posthorn.dev/getting-started/installation Commands to validate the configuration file and start the Posthorn HTTP listener using the standalone binary. ```bash posthorn validate --config posthorn.toml # check the config without serving posthorn serve --config posthorn.toml # start the HTTP listener ``` -------------------------------- ### Complete PostHog Configuration Example Source: https://posthorn.dev/configuration/reference This example demonstrates a full PostHog configuration, including two HTTP endpoints for a contact form and a transactional API, along with an SMTP listener. It covers logging, endpoint-specific settings like 'to', 'from', 'required' fields, email templating, transport configurations (Postmark, Resend), rate limiting, and SMTP listener settings such as TLS, authentication, and allowed senders. ```toml # Two HTTP endpoints (a contact form + a transactional api-mode endpoint) # plus an SMTP listener for Ghost/Gitea/etc. [logging] level = "info" format = "json" [[endpoints]] path = "/api/contact" to = ["alerts@example.com"] from = "Contact Form " required = ["name", "email", "message"] email_field = "email" honeypot = "_gotcha" allowed_origins = ["https://example.com", "https://www.example.com"] max_body_size = "64KB" trusted_proxies = ["cloudflare"] csrf_secret = "${env.CSRF_SECRET}" csrf_token_ttl = "30m" subject = "Contact: {{.name}}" body = """ From: {{.name}} <{{.email}}> {{.message}} """ redirect_success = "/thank-you" redirect_error = "/contact?error=1" [endpoints.transport] type = "postmark" [endpoints.transport.settings] api_key = "${env.POSTMARK_CONTACT_KEY}" [endpoints.rate_limit] count = 5 interval = "1m" # --- [[endpoints]] path = "/api/transactional" to = ["fallback@example.com"] from = "App " auth = "api-key" api_keys = ["${env.WORKER_KEY_PRIMARY}", "${env.WORKER_KEY_BACKUP}"] idempotency_cache_size = 5000 required = ["subject_line", "body"] subject = "{{.subject_line}}" body = "{{.body}}" [endpoints.transport] type = "resend" [endpoints.transport.settings] api_key = "${env.RESEND_API_KEY}" [endpoints.rate_limit] count = 100 interval = "1m" # --- [smtp_listener] listen = ":2525" require_tls = true tls_cert = "/etc/posthorn/cert.pem" tls_key = "/etc/posthorn/key.pem" auth_required = "smtp-auth" allowed_senders = ["*@example.com"] max_recipients_per_session = 10 max_message_size = "1MB" [[smtp_listener.smtp_users]] username = "ghost" password = "${env.GHOST_SMTP_PASSWORD}" [smtp_listener.transport] type = "postmark" [smtp_listener.transport.settings] api_key = "${env.POSTMARK_API_KEY}" ``` -------------------------------- ### Start and Monitor Posthorn Container Source: https://posthorn.dev/getting-started/quick-start Start the Posthorn container in detached mode and follow its logs to monitor its status and activity. ```bash docker compose up -d docker compose logs -f posthorn ``` -------------------------------- ### API Mode Response Example Source: https://posthorn.dev/reference/http-api An example of a successful HTTP 200 OK response from the API, indicating the submission status and ID. ```http HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 {"status":"ok","submission_id":"..."} ``` -------------------------------- ### Posthorn Service Startup Logs Source: https://posthorn.dev/recipes/multi-form-site Example log output from Posthorn upon successful startup, indicating that endpoints have been registered and the HTTP ingress is listening. ```log {"time":"...","level":"INFO","msg":"endpoint registered","path":"/api/contact","transport":"postmark","recipients":1} {"time":"...","level":"INFO","msg":"endpoint registered","path":"/api/feedback","transport":"postmark","recipients":1} {"time":"...","level":"INFO","msg":"endpoint registered","path":"/api/careers","transport":"postmark","recipients":1} {"time":"...","level":"INFO","msg":"http ingress listening","addr":":8080"} ``` -------------------------------- ### Start Posthorn Container (First Endpoint) Source: https://posthorn.dev/recipes/newsletter-signup Command to start the Posthorn Docker container in detached mode if it's the first endpoint being configured. ```bash docker compose up -d ``` -------------------------------- ### Trusted Proxies Configuration Source: https://posthorn.dev/configuration/reference Example of configuring trusted proxy IP ranges and presets for client IP handling. ```toml trusted_proxies = ["cloudflare", "10.0.0.0/8"] ``` -------------------------------- ### Set Environment Variables with systemd Source: https://posthorn.dev/security/api-keys Example of setting environment variables for Posthorn using systemd's EnvironmentFile directive. ```bash # systemd EnvironmentFile=/etc/posthorn/posthorn.env ``` -------------------------------- ### Configure a Transport Source: https://posthorn.dev/getting-started/concepts Specify the transport type and its settings for an endpoint. This example shows configuration for the Postmark transport. ```toml [endpoints.transport] type = "postmark" [endpoints.transport.settings] api_key = "${env.POSTMARK_API_KEY}" ``` -------------------------------- ### Configure Transport API Key Source: https://posthorn.dev/security/api-keys Example of configuring a transport API key using an environment variable placeholder in TOML. ```toml # Transport credential [endpoints.transport.settings] api_key = "${env.POSTMARK_API_KEY}" ``` -------------------------------- ### Display Posthorn Version Source: https://posthorn.dev/reference/cli Use the `version` command to display the installed version of Posthorn. This command is aliased to `--version` and `-v`. ```bash $ posthorn version posthorn v1.0.0 ``` -------------------------------- ### Set Environment Variables Directly Source: https://posthorn.dev/security/api-keys Example of setting environment variables directly when running Posthorn from the command line. ```bash # direct POSTMARK_API_KEY=abc123 posthorn serve --config ... ``` -------------------------------- ### Example of Go text/template Syntax Source: https://posthorn.dev/features/templating Illustrates common Go text/template syntax elements like variables, pipes, conditionals, and comments. These are used for rendering dynamic content in email subjects and bodies. ```go-template {{.name}} ``` ```go-template {{.email | html}} ``` ```go-template {{if .urgent}}!!!{{end}} ``` ```go-template {{/* this is a comment */}} ``` ```go-template {{if eq .category "bug"}} ``` -------------------------------- ### jq Queries for Docker Logs Source: https://posthorn.dev/reference/log-format Examples of using jq to parse and filter Docker logs for the 'posthorn' container. These snippets demonstrate ad-hoc analysis for errors, latency, and specific events. ```bash # Tail and pretty-print docker logs -f posthorn | jq . ``` ```bash # All failures from the last hour docker logs --since 1h posthorn | jq 'select(.level=="ERROR")' ``` ```bash # Latency P99 over the last 1000 sent submissions docker logs --tail 10000 posthorn \ | jq -s 'map(select(.msg=="submission_sent")) | sort_by(.latency_ms) | .[-10:]' ``` ```bash # Find a specific submission across all log lines docker logs posthorn | jq 'select(.submission_id=="7f2c84d6-9b1e-4c2f-a3b8-1a2b3c4d5e6f")' ``` ```bash # Find a specific SMTP session across all log lines docker logs posthorn | jq 'select(.session_id=="...")' ``` -------------------------------- ### Configure log rotation for standalone Posthorn logs Source: https://posthorn.dev/deployment/reading-logs Example `logrotate` configuration for managing log files generated by a standalone Posthorn binary. ```logrotate /var/log/posthorn.log { daily rotate 14 compress missingok notifempty copytruncate } ``` -------------------------------- ### TOML Configuration with API Key Placeholder Source: https://posthorn.dev/configuration/environment-variables Use `${env.VAR}` syntax to inject environment variables into TOML string fields. This example shows how to configure an API key. ```toml api_key = "${env.POSTMARK_API_KEY}" ``` -------------------------------- ### Configure API-mode Bearer Tokens Source: https://posthorn.dev/security/api-keys Example of configuring API-mode bearer tokens using environment variable placeholders in TOML. ```toml # API-mode bearer tokens [[endpoints]] auth = "api-key" api_keys = ["${env.WORKER_KEY_PRIMARY}", "${env.WORKER_KEY_BACKUP}"] ``` -------------------------------- ### Set Environment Variables with Docker Compose Source: https://posthorn.dev/security/api-keys Example of setting environment variables for Posthorn using a Docker Compose file. ```bash # Docker Compose docker compose --env-file .env up -d ``` -------------------------------- ### Configure Reply-To Email and Body Template Source: https://posthorn.dev/features/validation Example TOML configuration for setting required fields, the reply-to email field, and a body template that includes the sender's name and email. ```toml [[endpoints]] required = ["name", "email", "message"] reply_to_email_field = "email" subject = "Contact from {{.name}}" body = """ From: {{.name}} <{{.email}}> {{.message}} """ ``` -------------------------------- ### Configure SMTP User Password Source: https://posthorn.dev/security/api-keys Example of configuring an SMTP user password using an environment variable placeholder in TOML. ```toml # SMTP user password [[smtp_listener.smtp_users]] username = "ghost" password = "${env.GHOST_SMTP_PASSWORD}" ``` -------------------------------- ### Configure CSRF HMAC Key Source: https://posthorn.dev/security/api-keys Example of configuring a CSRF HMAC key using an environment variable placeholder in TOML. ```toml # CSRF HMAC key csrf_secret = "${env.CSRF_SECRET}" ``` -------------------------------- ### Successful Validation Output Source: https://posthorn.dev/reference/cli Displays a success message when the configuration is valid and the listener is ready to start. Exit code 0 indicates success. ```bash $ posthorn validate --config posthorn.toml config OK: 2 endpoint(s) ``` -------------------------------- ### Multipart POST Request Example Source: https://posthorn.dev/reference/http-api This snippet demonstrates a POST request using `multipart/form-data` for a form-mode endpoint. It illustrates how form fields are structured within the multipart body, separated by a boundary. ```http POST /api/contact HTTP/1.1 Host: example.com Content-Type: multipart/form-data; boundary=----WebKitFormBoundary ------WebKitFormBoundary Content-Disposition: form-data; name="name" Alice Smith ------WebKitFormBoundary Content-Disposition: form-data; name="email" alice@example.com ------WebKitFormBoundary Content-Disposition: form-data; name="message" Hello world ------WebKitFormBoundary-- ``` -------------------------------- ### Posthorn Service Logs Source: https://posthorn.dev/recipes/hugo-comentario Example log output from Posthorn indicating successful registration of HTTP and SMTP listeners, and endpoint registration for Postmark. ```json {"msg":"endpoint registered","path":"/api/contact","transport":"postmark","recipients":1} {"msg":"smtp_listener registered","listen":":2525","transport":"postmark","smtp_users":0} {"msg":"http ingress listening","addr":":8080"} {"msg":"smtp ingress listening","addr":":2525"} ``` -------------------------------- ### Example Idempotency-Key Request Source: https://posthorn.dev/features/api-mode This cURL command demonstrates how to send a POST request with the `Idempotency-Key` header to the transactional API. Ensure the key is a printable ASCII string. ```bash curl -X POST https://yourdomain.com/api/transactional \ -H "Authorization: Bearer $WORKER_KEY_PRIMARY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: ord-12345-welcome-email" \ --data '{"subject_line":"Welcome","message":"..."}' ``` -------------------------------- ### Subject Template Parse Error Example Source: https://posthorn.dev/features/templating Demonstrates a typical error message when a subject template fails to parse during configuration validation. Syntax errors are caught at deploy time. ```bash $ posthorn validate --config posthorn.toml ERROR: endpoint 0 (/api/contact): subject template parse error at line 1: function "foo" not defined exit status 1 ``` -------------------------------- ### Build Posthorn from Repository Source: https://posthorn.dev/deployment/binary Clone the Posthorn repository and build the binary locally. ```bash git clone https://github.com/craigmccaskill/posthorn cd posthorn go build -o posthorn ./core/cmd/posthorn ``` -------------------------------- ### Create Project Directory Source: https://posthorn.dev/getting-started/quick-start Create a new directory for your Posthorn project and navigate into it. ```bash mkdir posthorn-test && cd posthorn-test ``` -------------------------------- ### Successful Submission Response Source: https://posthorn.dev/recipes/contact-form Example of a successful HTTP response from Posthorn after a valid form submission. ```http HTTP/2 200 content-type: application/json; charset=utf-8 {"status":"ok","submission_id":"7f2c84d6-9b1e-4c2f-a3b8-1a2b3c4d5e6f"} ``` -------------------------------- ### Ghost SMTP Configuration Example Source: https://posthorn.dev/features/smtp-ingress Shows environment variables or config keys for configuring Ghost to use Posthorn's SMTP listener. Ensure mail__from matches Posthorn's allowed_senders and auth credentials align with smtp_listener.smtp_users. ```env mail__from='"Ghost" ' mail__transport=SMTP mail__options__host=posthorn.yourdomain.com mail__options__port=2525 mail__options__secure=false # using STARTTLS, not implicit TLS mail__options__auth__user=ghost mail__options__auth__pass="$GHOST_SMTP_PASSWORD" ``` -------------------------------- ### Create Posthorn Directory Source: https://posthorn.dev/recipes/contact-form Initializes the directory structure for the Posthorn deployment. ```bash mkdir -p posthorn && cd posthorn ``` -------------------------------- ### Display Posthorn Help Information Source: https://posthorn.dev/reference/cli Use the `help` command to display general usage information for the Posthorn CLI. Subcommand-specific help is not separately available; the top-level help lists flags for each subcommand. ```bash $ posthorn help posthorn — outbound mail gateway Usage: posthorn serve [--config ] [--listen ] posthorn validate [--config ] posthorn version posthorn help Examples: posthorn serve --config /etc/posthorn/config.toml posthorn validate --config ./posthorn.toml ``` -------------------------------- ### Add SPF Record (TXT) Source: https://posthorn.dev/security/dns Configure a TXT record for SPF. If an SPF record already exists, merge the Postmark include with existing ones instead of creating a duplicate. ```dns Type: TXT Host: @ (or example.com — depends on DNS provider) Value: v=spf1 a mx include:spf.mtasv.net ~all ``` ```dns v=spf1 a mx include:_spf.google.com include:spf.mtasv.net ~all ``` -------------------------------- ### Caddy Reverse Proxy Configuration Source: https://posthorn.dev/getting-started/quick-start Configure Caddy to proxy requests to the Posthorn API. This setup is suitable for static sites. ```caddy example.com { reverse_proxy /api/contact* posthorn:8080 file_server } ``` -------------------------------- ### Honeypot HTML Input Field Source: https://posthorn.dev/features/spam-protection Example of an HTML input field designed to be hidden from users but visible to bots, acting as a honeypot. ```html ``` -------------------------------- ### Add DMARC Record (TXT) - Initial Monitoring Source: https://posthorn.dev/security/dns Configure a TXT record for DMARC with 'p=none' for monitoring without enforcement. This is recommended before moving to stricter policies. ```dns v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com ``` -------------------------------- ### Cron Job for Simple Alerting Source: https://posthorn.dev/recipes/monitoring-alerts A cron job example that checks a service's health and sends a DOWN alert via Posthorn if it fails. ```bash */5 * * * * curl -s --fail https://api.yourservice.com/healthz || \ curl -s -X POST https://yourdomain.com/api/alert \ --data-urlencode "service=api.yourservice.com" \ --data-urlencode "status=DOWN" \ --data-urlencode "details=healthz check failed at $(date -Iseconds)" ``` -------------------------------- ### Environment Variable Configuration for Postmark API Keys Source: https://posthorn.dev/recipes/multi-form-site Sets up environment variables for Postmark API keys, supporting both a single token for all endpoints or individual tokens for each. ```dotenv # One-token shape: # POSTMARK_API_KEY=your-token # Or three-token shape: POSTMARK_CONTACT_KEY=your-contact-server-token POSTMARK_FEEDBACK_KEY=your-feedback-server-token POSTMARK_CAREERS_KEY=your-careers-server-token ``` -------------------------------- ### Custom Fields Passthrough Example Source: https://posthorn.dev/getting-started/concepts Demonstrates how additional form fields beyond the named fields are included in the email body under 'Additional fields'. ```text From: Alice This is the message body the template rendered. Additional fields: company: Acme Corp source: HN newsletter_opt_in: yes ``` -------------------------------- ### Posthorn Configuration (posthorn.toml) Source: https://posthorn.dev/recipes/ghost Sets up Posthorn's SMTP listener to accept mail from Ghost. Configures sender allowlist, message size, and transport settings for Postmark. ```toml [smtp_listener] listen = ":2525" auth_required = "none" require_tls = false allowed_senders = ["*@example.com"] max_recipients_per_session = 50 # higher than default; newsletter sends batch max_message_size = "10MB" # bigger than default; newsletters embed images [smtp_listener.transport] type = "postmark" [smtp_listener.transport.settings] api_key = "${env.POSTMARK_API_KEY}" ``` -------------------------------- ### Example Submission ID in JSON Response Source: https://posthorn.dev/getting-started/concepts A sample JSON response containing the generated submission ID, used for correlating log events. ```json {"submission_id":"7f2c84d6-9b1e-4c2f-a3b8-1a2b3c4d5e6f", ...} ``` -------------------------------- ### Request Pipeline Stages Source: https://posthorn.dev/getting-started/concepts Illustrates the sequential processing steps for form submissions, from body size capping to response logging. ```text 1. body size cap → http.MaxBytesReader wraps r.Body (413) 2. method check → POST only (405) 3. content-type check → form-encoded only (400) 4. origin/referer check → fail-closed if allowed_origins set (403) 5. rate limit check → token bucket, proxy-aware IP (429) 6. parse form → r.ParseForm() reads body (413/400) 7. honeypot check → silent 200 if field non-empty (200) 8. required fields → all listed fields present + non-empty (422) 9. email format → submitter email field syntactic (422) 10. generate submission ID (UUIDv4), log "submission_received" 11. render subject template 12. render body template + custom-fields passthrough 13. transport.Send() with retry policy 14. log outcome, write response (JSON or redirect) (200/502) ``` -------------------------------- ### Configuring a Transport Type Source: https://posthorn.dev/configuration/transports Example TOML configuration for setting the email transport type for an endpoint. Supports Postmark, Resend, Mailgun, SES, and SMTP. ```toml [endpoints.transport] type = "postmark" # or "resend", "mailgun", "ses", "smtp" [endpoints.transport.settings] # Per-transport settings, see the matching page below. ``` -------------------------------- ### 303 See Other Redirect Source: https://posthorn.dev/reference/response-codes Returned after content negotiation chooses an HTML-friendly response, typically for form submissions. Ensures the browser issues a GET to the redirect URL. ```HTTP HTTP/1.1 303 See Other Location: /thank-you ``` -------------------------------- ### Add DKIM Record (TXT) Source: https://posthorn.dev/security/dns Set up a TXT record for DKIM using the selector and public key provided by Postmark. The selector value will vary. ```dns Type: TXT Host: 20240101pm._domainkey (Postmark provides the selector — varies) Value: k=rsa; p=MIGfMA0GCSq... (long key, Postmark provides full value) ``` -------------------------------- ### 500 Internal Server Error Example Source: https://posthorn.dev/reference/response-codes This snippet shows the structure of a 500 Internal Server Error response, indicating a Posthorn-internal failure such as a template rendering issue. ```http HTTP/1.1 500 Internal Server Error Content-Type: text/plain; charset=utf-8 submission could not be processed ``` -------------------------------- ### Ghost Environment Configuration (.env.ghost) Source: https://posthorn.dev/recipes/ghost Configures Ghost's mail settings to use Posthorn as the SMTP server. Ensure mail.from matches Posthorn's allowed_senders. ```env url=https://blog.example.com database__client=mysql database__connection__host=ghost-db database__connection__user=ghost database__connection__password=${GHOST_DB_PASSWORD} database__connection__database=ghost # Mail config. Posthorn is the SMTP server. mail__transport=SMTP mail__from="Blog " mail__options__host=posthorn mail__options__port=2525 mail__options__secure=false mail__options__ignoreTLS=true # mail__options__auth__user and mail__options__auth__pass intentionally omitted. ``` -------------------------------- ### Live tail Kubernetes logs Source: https://posthorn.dev/deployment/reading-logs Continuously stream logs from a Posthorn deployment in Kubernetes. ```bash kubectl logs -f deployment/posthorn ``` -------------------------------- ### Custom Fields Passthrough Example Source: https://posthorn.dev/features/templating Custom fields submitted via a form are automatically appended to the email body in a sorted list. Empty fields are omitted. ```text [rendered body template] Additional fields: company: Acme Corp source: HN newsletter_opt_in: yes ``` -------------------------------- ### 303 See Other Source: https://posthorn.dev/reference/response-codes Indicates a redirect after content negotiation chose an HTML-friendly response. This is typically used after form submissions where the browser should perform a GET request to the specified location. ```APIDOC ## 303 See Other ### Description Indicates a redirect after content negotiation chose an HTML-friendly response. This is typically used after form submissions where the browser should perform a GET request to the specified location. ### Method GET (implicitly, by browser behavior) ### Endpoint N/A (Response to a previous request) ### Response #### Success Response (303) - **Location** (string) - The URL to redirect to. ### Response Example ```http HTTP/1.1 303 See Other Location: /thank-you ``` ### Returned When * Client prefers `text/html` (e.g., default browser form submission). * `redirect_success` or `redirect_error` is configured for the endpoint. * The submission has been processed (successfully or not). **Note:** The 303 status ensures the browser issues a GET to the redirect URL, preventing form re-submission on refresh. ``` -------------------------------- ### Create Unprivileged User for Posthorn Source: https://posthorn.dev/deployment/binary Create a system user for running the Posthorn service. ```bash sudo useradd --system --no-create-home --shell /usr/sbin/nologin posthorn ``` -------------------------------- ### HTTP 429 Response Example Source: https://posthorn.dev/features/rate-limiting Posthorn returns an HTTP 429 status code with a plaintext body when a rate limit is exceeded. Clients should implement their own backoff strategy. ```http HTTP/1.1 429 Too Many Requests Content-Type: text/plain; charset=utf-8 rate limit exceeded ```