Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions docs/adr/003-rabbitmq-settings-publishing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# ADR: Replace Common Settings HTTP API with RabbitMQ Publishing

## Status

**Proposed**

## Context

The cozy-stack currently synchronizes user settings with an external "common settings" application via HTTP API calls.
This integration is implemented in `model/settings/common/common.go` and performs:

1. **HTTP POST** to `/api/admin/user/settings` when creating settings
2. **HTTP GET** to `/api/admin/user/settings/{nickname}` to check remote version
3. **HTTP PUT** to `/api/admin/user/settings/{nickname}` to update settings

This approach has several issues:

- **Synchronous blocking**: HTTP calls block the settings update operation
- **Version conflict complexity**: The code checks remote versions before updates, creating complex conflict resolution logic
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was done by purpose. Because we need to have a source of truth.

I'm not sure we're dealing with that correctly from the common-settings app side right now.

If we have 2 app sending a message to update CS, we'll have an issue on CS side. And this cozy-stack today is the entry point of CS, the check was done there.

So if we remove this check, we need to be sure that CS can handle this stuff. cc @rezk2ll

Copy link
Contributor

@rezk2ll rezk2ll Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the version control is done in CS for incoming requests.

but IMO, the CS app can retire and the stack will handle it. so in reality, we will have only one app sending the message updates.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is CS app?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

linagora/twake-workplace-common-settings

- **Tight coupling**: Direct HTTP dependency on the common settings app
- **Failure propagation**: HTTP failures can affect the main settings update flow
- **No longer needed**: The common settings app is being deprecated, so the stack no longer needs to support conflict resolution

The codebase already has a mature RabbitMQ infrastructure for message consumption (password updates, user creation, phone updates, subscription changes, app lifecycle).
This infrastructure can be extended for publishing.

## Proposal

Replace the HTTP API calls with RabbitMQ message publishing using a fire-and-forget pattern:

### Architecture

```mermaid
flowchart LR
subgraph CozyStack["cozy-stack"]
Publisher["Publisher"]
end

subgraph Storage["Storage"]
CouchDB["CouchDB<br/>(settings)"]
end

subgraph MessageBroker["Message Broker"]
RabbitMQ["RabbitMQ<br/>(exchange: settings)"]
end

subgraph Consumer["TWP app"]
CommonSettings["Common Settings<br/>(consume)"]
end

Publisher -->|"1. Update settings"| CouchDB
Publisher -->|"2. Publish message"| RabbitMQ
RabbitMQ -->|"user.settings.updated"| CommonSettings
```

### Key Changes

1. **New Publisher Infrastructure** (`pkg/rabbitmq/publisher.go`)
- Reuses existing `RabbitMQConnection` for connection management
- Provides thread-safe publishing with automatic channel recovery
- JSON serialization with persistent delivery mode

2. **Service Layer Extension** (`pkg/rabbitmq/service.go`)
- Add publishing capabilities alongside existing consumers
- Context-aware publisher selection (same as consumers)

3. **Simplified Common Settings Module** (`model/settings/common/common.go`)
- Replace HTTP calls with RabbitMQ publishing
- Remove version conflict checking (local DB is source of truth)
- Fire-and-forget: log errors but don't fail the operation

### Configuration

```yaml
rabbitmq:
enabled: true
nodes:
default:
url: amqp://guest:guest@localhost:5672/
publishing:
common_settings:
exchange: "settings"
routing_key: "user.settings.updated"
```

### Message Format

Preserve existing message structure for backward compatibility:

```json
{
"source": "cozy-stack",
"nickname": "user-slug",
"request_id": "domain_1234567890",
"timestamp": 1234567890,
"version": 2,
"payload": {
"language": "en",
"timezone": "Europe/Paris",
"first_name": "John",
"last_name": "Doe",
"display_name": "John Doe",
"email": "[email protected]",
"phone": "+33612345678",
"matrix_id": "@john:example.com",
"avatar": "https://cozy.example.com/public/avatar?v=2"
}
}
```

## Alternatives

## Decision

## Consequences