Skip to content

Security configuration ​

Spine v0.6.0 applies encrypted broker connections and HTTP and WebSocket size, time, and connection limits by default. Use this reference to check defaults and configuration constraints, then choose the origins, authentication, and message failure policies for your deployed application.

Configuration validation ​

App.Validate checks boot options without opening network connections. App.Run performs the same checks before startup. These preliminary checks do not establish whether constructor and dependency configuration or broker connections will succeed.

go
if err := app.Validate(options); err != nil {
    log.Fatal(err)
}
if err := app.Run(options); err != nil {
    log.Fatal(err)
}

Errors are returned as *boot.ConfigError. Each ConfigIssue in Issues provides the following fields. Use Code to classify errors programmatically instead of parsing the message. Credentials in broker URLs are not exposed in configuration errors.

FieldMeaning
PathLocation of the invalid configuration
CodeError classification code
MessageHuman-readable error description
HintHow to fix the configuration

Secure defaults ​

AreaDefault behaviorHow to adjust
HTTPRead and write timeouts, header and body size limitsboot.HTTPOptions
KafkaTLS 1.2 or later for enabled consumers and publishersTLS, Dialer, Transport. For local plaintext connections, use AllowInsecureTransport: true
RabbitMQ connectionsamqps:// requiredFor local plaintext connections, use amqp:// and AllowInsecureTransport: true
RabbitMQ publishingPersistent messages, mandatory routing, publisher confirms, finite retriesPublisherRetry
RabbitMQ processing failuresReject without requeueingFailurePolicy
RabbitMQ concurrent processing1 unacknowledged message per consumerPositive PrefetchCount
Consumer transport failuresRecreate the reader with exponential backoff and jitterConsumerRetry
WebSocket originsBrowser requests allowed only from the same scheme and hostAllowedOrigins
WebSocket connections1024 established connections and pending connection requests combinedPositive MaxConnections or boot.UnlimitedWebSocketConnections
WebSocket connection authenticationOnly interceptors implementing the optional interface run before the upgradeImplement core.WebSocketHandshakeInterceptor
Global interceptorsApply to both HTTP and WebSocketLimit the scope with InterceptorFor
CORS with credentialsExplicit origins requiredCannot combine * with AllowCredentials: true
CookiesReject invalid fields before writing the responseUse httpx.EncodeCookieValue when arbitrary values must be preserved

Kafka ​

Enable Read and Write in boot.KafkaOptions independently. Configuring either requires Brokers; using Read also requires GroupID. TLS 1.2 or later is applied to both consumers and publishers unless separate transport settings are provided.

go
Kafka: &boot.KafkaOptions{
    Brokers: []string{"kafka.example:9093"},
    Read:  &boot.KafkaReadOptions{GroupID: "orders"},
    Write: &boot.KafkaWriteOptions{},
}

Configure TLS, Dialer, and Transport if you need a custom CA, mTLS, or SASL. Configuring both explicit TLS and AllowInsecureTransport for an enabled role produces a KAFKA_TLS_INSECURE_CONFLICT error. Explicitly allow plaintext connections only in an isolated local development environment.

go
Kafka: &boot.KafkaOptions{
    Brokers:                []string{"localhost:9092"},
    AllowInsecureTransport: true,
    Read:                   &boot.KafkaReadOptions{GroupID: "local"},
}

After a reader error or ACK/NACK failure, the existing reader is closed and a new one is created according to ConsumerRetry. This option controls transport reconnection; it does not change the handler's message processing policy.

ConsumerRetryOptions fieldTypeDefault
InitialDelaytime.Duration100ms
MaxDelaytime.Duration5 seconds
Multiplierfloat642
Jitterfloat640.2, or 20%
MaxAttemptsint0, unlimited retries

0 selects the default for each field. Negative values are not allowed. An explicit maximum delay must be at least the initial delay; the multiplier must be at least 1, and jitter must be between 0 and 1. The same options apply to RabbitMQ consumers.

Implement Kafka handlers idempotently to account for redelivery. Permanently unprocessable messages are not automatically skipped or sent to a separate failure queue.

RabbitMQ ​

Use amqps:// for production URLs. Each enabled Read and Write requires Exchange. The default failure policy rejects messages without requeueing.

go
RabbitMQ: &boot.RabbitMqOptions{
    URL: "amqps://user:pass@rabbit.example/vhost",
    PublisherRetry: boot.PublisherRetryOptions{
        InitialDelay:   200 * time.Millisecond,
        MaxDelay:       5 * time.Second,
        MaxAttempts:    5,
        ConfirmTimeout: 5 * time.Second,
    },
    Read: &boot.RabbitMqReadOptions{
        Exchange:      "events",
        PrefetchCount: 16,
        FailurePolicy: boot.RabbitMqFailureReject,
        DeadLetter: &boot.RabbitMqDeadLetterOptions{
            Exchange:   "events.dlx",
            RoutingKey: "events.failed",
        },
    },
    Write: &boot.RabbitMqWriteOptions{Exchange: "events"},
}
Read settingDefault and constraints
PrefetchCount0 means 1 unacknowledged message per consumer. Must be positive when explicitly set
FailurePolicyboot.RabbitMqFailureReject if omitted. Use boot.RabbitMqFailureRequeue to requeue
DeadLetterDefault: nil. When configured, requires Exchange for a pre-created DLX; RoutingKey is also optional
RequeueOnErrorCompatibility with earlier versions. true requeues. A conflict with an explicit FailurePolicy produces a configuration error

A DLX is an exchange that receives messages that could not be processed. An operator must create it first. Without a matching DLX configuration, rejected messages may be discarded. If an existing queue's arguments differ from the new settings, RabbitMQ may return a queue declaration precondition failure.

RabbitMqFailureRequeue can redeliver unprocessable messages indefinitely. Use the reject policy and a DLX for new configurations. RequeueOnError is planned for deprecation; use FailurePolicy in new code. Handlers are selected by the RoutingKey used to bind the queue, rather than the AMQP Type.

The publisher uses persistent messages, mandatory=true, and publisher confirms. It does not treat publishing as successful if a message cannot be routed or a confirm is rejected or lost. Retrying after a lost confirm can publish the same message more than once, so consumers must be idempotent.

PublisherRetryOptions fieldTypeDefault
InitialDelaytime.Duration100ms
MaxDelaytime.Duration5 seconds
Multiplierfloat642
Jitterfloat640.2, or 20%
MaxAttemptsint3 attempts in total
ConfirmTimeouttime.Duration5 seconds

0 selects the default for each field. Negative values are not allowed. Delay range, multiplier, and jitter constraints are the same as for ConsumerRetryOptions. Consider the possibility of duplicate publishing when increasing MaxAttempts.

WebSocket ​

Configure origin, connection, and message limits in boot.HTTPOptions.WebSocket. Specify the exact origins and connection limit for your application.

go
HTTP: &boot.HTTPOptions{
    WebSocket: boot.WebSocketOptions{
        AllowedOrigins: []string{"https://app.example.com"},
        MaxConnections: 4096,
    },
},
WebSocketOptions fieldTypeDefault and constraints
MaxConnectionsint0 means boot.DefaultWebSocketMaxConnections, or 1024. Set a positive limit or disable it with boot.UnlimitedWebSocketConnections
CapacityRetryAftertime.Duration5 seconds. Retry-After value when the connection limit is exceeded. Must not be negative
AllowedOrigins[]stringSame origin if empty. Explicit values must be http:// or https:// origins without paths, queries, or fragments. * is also supported, but allows all origins
TrustedProxyCIDRs[]stringNo default. List of proxy CIDRs whose forwarded headers are trusted
MaxMessageBytesint641 MiB. Must not be negative
HandshakeTimeouttime.Duration10 seconds. Must not be negative
ReadTimeouttime.Duration60 seconds. Must not be negative
WriteTimeouttime.Duration10 seconds. Must not be negative
PingIntervaltime.DurationHalf the read timeout. 30 seconds with the default read timeout. Must not be negative

0 selects the default for time and size fields. To disable the connection limit, use the named constant boot.UnlimitedWebSocketConnections instead of -1.

If AllowedOrigins is empty, browser requests must have the same scheme and host. You can set TrustedProxyCIDRs behind a TLS termination proxy. Forwarded and X-Forwarded-Proto are trusted only when the request's peer is in this list. The proxy must remove or overwrite forwarded headers received from external clients.

Connection requests waiting for authentication count toward the connection limit. Requests above the limit are not upgraded; they receive HTTP 503, a Retry-After header, and JSON containing the code WEBSOCKET_CAPACITY_EXCEEDED.

If connection authentication is required, also implement core.WebSocketHandshakeInterceptor on the interceptor. PreHandshake runs after a connection slot is reserved and before the HTTP upgrade. It is separate from the per-message PreHandle.

go
func (i *AuthInterceptor) PreHandshake(
    ctx core.WebSocketHandshakeContext,
    meta core.HandlerMeta,
) error {
    token := ctx.Header("Authorization")
    if token == "" {
        return httperr.Unauthorized("missing credentials")
    }
    return i.verify(token)
}

Contexts used before connection establishment and message contexts preserve the original request's headers, queries, cookies, remote address, host, and request URI. For the public interfaces, see the execution context reference.

CORS and cookies ​

You cannot combine AllowOrigins: []string{"*"} with AllowCredentials: true. Use cors.NewValidated to validate this at startup. cors.New also returns a structured error on the first request before writing response headers.

go
corsInterceptor, err := cors.NewValidated(cors.Config{
    AllowOrigins:     []string{"https://app.example.com"},
    AllowCredentials: true,
})
if err != nil {
    log.Fatal(err)
}
app.InterceptorFor(boot.InterceptorHTTP, corsInterceptor)

If a cookie's name, value, path, domain, SameSite, or Priority is invalid, JSON, string, binary, and redirect responses fail before writing headers or the body. Use httpx.EncodeCookieValue and httpx.DecodeCookieValue when Unicode or delimiters must be restored to their original values.

Operational constraints ​

  • Monitor WEBSOCKET_CAPACITY_EXCEEDED, exhausted consumer reconnection attempts, and RabbitMQ Type and RoutingKey mismatches.
  • Implement Kafka and RabbitMQ consumers idempotently. Prepare operational procedures for unprocessable Kafka messages and RabbitMQ DLXs as well.
  • Actual socket writes and broker publishing are not atomic with DB commits. If atomicity is required, use a transactional outbox and idempotency keys.
  • Check supported versions in the current release.