Charm architecture

bingo is a Go application providing paste creation, retrieval, and expiry - a self-hosted pastebin service for Canonical. The charm is built with the go-framework Charmcraft extension, part of the 12-factor app support provided by Charmcraft, Rockcraft, and paas-charm.

The general container/sidecar layout, Pebble usage, and PaasCharm base class shared by every 12-factor app charm are already documented centrally:

This page only covers what’s specific to bingo’s usage of go-framework.

bingo container

bingo’s workload container (Kubernetes container name app, per the go-framework extension) runs the compiled bingo binary (built from cmd/bingo/) directly under Pebble. The process runs as the _daemon_ user with working directory /app, and listens on port 8080 by default (the app-port framework configuration).

The binary reads its configuration from environment variables: charm configuration options (for example base-url, max-paste-size-bytes, log-level, web-dir) are injected as APP_*, while integration data is injected using each integration’s own convention. For example, POSTGRESQL_DB_CONNECT_STRING is used for the database, and standard OTEL_* variables are used for tracing.

The image is defined in rockcraft.yaml at the repository root, using the go-framework Rockcraft extension with two customizations: the main package lives at cmd/bingo/ rather than the module root, and the production Vite build of the React frontend is staged into the image under /app/web/dist, served by the binary as a single-page application when the web-dir configuration option is set.

Charm code overview

charm/src/charm.py defines the BingoCharm class, which inherits from paas_charm.go.Charm (itself a PaasCharm subclass; see the generic charm code overview for how PaasCharm.__init__ wires up event observers).

BingoCharm adds two customizations on top of the inherited behavior:

  • It observes config_changed a second time (after the parent class’s own handler) to block the unit if oauth-redirect-path has been changed away from its required fixed value (/auth/callback) - bingo’s OIDC callback route is hardcoded and does not read that configuration option.

  • It overrides the _base_url property so that the base-url configuration option, when set, takes priority over the ingress-derived URL that paas_charm.go.Charm._base_url would otherwise always return, letting operators control the externally-visible link text used in generated paste URLs.

Juju events

bingo’s charmcraft.yaml declares postgresql, tracing, and oauth under requires, alongside the logging/ingress/secret-storage relations injected automatically by the go-framework extension. Of all the integration-related events listed in the generic Juju events reference, only the ones tied to those relations are ever fired for bingo:

  • PostgreSQL (required): database_created, endpoints_changed, database_relation_broken

  • OAuth (optional): oauth_info_changed, oauth_info_removed

  • Tracing (optional): tracing_endpoint_changed, tracing_endpoint_removed

  • Ingress (auto-injected): ingress_ready, ingress_revoked

  • rotate_secret_key

Events tied to the other integrations paas-charm supports never occur, since bingo’s charmcraft.yaml doesn’t declare those relations.

On top of the generic response described in the events reference, two behaviors are specific to bingo:

  • Every config_changed event also validates oauth-redirect-path: if it has been changed away from its required fixed value, the unit is blocked (see Charm code overview above).

  • No event ever triggers paas-charm’s migration runner, since bingo ships no migrate script in its image - the bingo binary instead applies its own migrations internally on every process start (see database.Migrate in cmd/bingo/main.go).

See Relation endpoints for bingo’s full list of relations, and Charm for more on the charm lifecycle in general.