== Messaging

Fedora uses many event-driven services triggered by messages. In the
past, this was done with ZeroMQ and
https://fedmsg.readthedocs.io/en/stable/[fedmsg]. This has been replaced
by an AMQP message broker and the
https://fedora-messaging.readthedocs.io/en/latest/[fedora-messaging] for
Python applications. This documentation outlines the policies for
sending and receiving messages. To learn how to send and receive
messages, see the https://fedora-messaging.readthedocs.io/en/latest[fedora-messaging documentation].

=== Broker URLs

The broker consists of multiple RabbitMQ nodes. They are available
through the proxies at `amqps://rabbitmq.fedoraproject.org` for
production and `amqps://rabbitmq.stg.fedoraproject.org` for staging.
Clients can connect using these URLs both inside and outside the Fedora
VPN, but users outside need to use a separate virtual host. Consult the
https://fedora-messaging.readthedocs.io/en/latest/user-guide/quick-start.html#fedora-s-public-broker[fedora-messaging
documentation] for details on how to connect externally.

=== Identity

In order to help with debugging, clients must configure the
https://fedora-messaging.readthedocs.io/en/latest/user-guide/configuration.html#client-properties[client_properties]
option to include their application name under the `app` key. Clients
should include the application version, if possible, in the
`app_version` key.

=== Authentication

When applications are deployed, clients must authenticate with the
message broker over a TLS connection using x509 certificates. There are
https://fedora-messaging.readthedocs.io/en/stable/user-guide/configuration.html#tls[configuration
options] for this in fedora-messaging.

Clients require certificates issued by Fedora Infrastructure. If you're
not using the external, read-only user, file a
https://pagure.io/fedora-infrastructure/issues[ticket] requesting a
certificate for the AMQP broker and be sure to provide the username you
plan to use. This is placed in the Common Name of the client certificate
and must match the name of the user you create in AMQP. Consult the
Authorization section below for details on creating users, queues, and
bindings.

=== Authorization

The message broker can use https://www.rabbitmq.com/vhosts.html[virtual
hosts] to allow multiple applications to use the broker. The general
purpose publish-subscribe virtual host is called `/pubsub` and has its
authorization policy outlined below. If your application is using a
different virtual host for private messaging (for example, your
application uses Celery), different authorization rules apply.

==== pubsub Virtual Host

AMQP clients do not have permission to create exchanges, queues, or
bindings. However, they can and should declare the exchanges, queues,
and bindings they expect to exist in their fedora-messaging
configuration so that if they do not exist, the application will fail
with a helpful error message about which resource is not available.

[WARNING]
====
Because AMQP clients don't have permission to create objects, you need
to set
https://fedora-messaging.readthedocs.io/en/stable/user-guide/configuration.html#passive-declares["passive_declares
= true"] or you will get 403 Permission Denied errors.
====

Users, exchanges, queues, bindings, and
https://www.rabbitmq.com/vhosts.html[virtual hosts] other objects are
managed in the broker using the Fedora Infrastructure Ansible project
and must be declared there.

To do so, you can use the `rabbit/queue` role in the Ansible repository.
An example usage in your deployment playbook would be:

....
roles:
  - role: rabbit/queue
    username: bodhi
    queue_name: bodhi_masher
    routing_keys:
      - "routing_key1"
      - "routing_key2"
....

Note that users only have permissions to read from queues prefixed with
their name so if your username is "bodhi", all queues must start with
"bodhi". The username must also match the common name in the x509
certificate used for authentication.

If you want to create a user that will only need to publish messages,
and not consume them, you can use the `rabbit/user` role in the Ansible
repository. An example usage in your deployment playbook would be:

....
roles:
  - role: rabbit/user
    username: bodhi
....

Please note that the username must match the TLS certificate's Common
Name, and they were created with the environment suffix. As a result, if
you want the username to match in staging too, you should use:

....
username: "bodhi{{ env_suffix}}"
....

===== Bindings

Messages from AMQP publishers are sent to the `amq.topic` exchange.
Messages from ZeroMQ publishers are sent to the `zmq.topic` exchange. In
order to receive all messages during the transition period from ZeroMQ
to AMQP, be sure to bind your consumers to both exchanges:

....
[[bindings]]
queue = "your queue"
exchange = "amq.topic"
routing_keys = ["key1", "key2"]

[[bindings]]
queue = "your queue"
exchange = "zmq.topic"
routing_keys = ["key1", "key2"]
....
