# How CasaOS Implements Its Notification System: Architecture and Code Deep Dive

> Discover how CasaOS implements its notification system. Explore the architecture using GORM, MessageBus, and WebSockets for persistent alerts and real-time UI updates.

- Repository: [IceWhale/CasaOS](https://github.com/IceWhaleTech/CasaOS)
- Tags: architecture
- Published: 2026-06-26

---

**CasaOS implements its notification system using a service-oriented architecture that combines a GORM-backed SQLite database, a MessageBus pub/sub dispatcher, and WebSocket broadcasting to deliver both persistent alerts and real-time UI updates.**

The notification subsystem in CasaOS (IceWhaleTech/CasaOS) provides the backbone for user alerts, file operation progress, and system status messages. Written in Go, this system leverages a clean interface-based design to decouple storage from delivery mechanisms, ensuring reliable message persistence while supporting instantaneous front-end updates.

## Core Architecture Overview

CasaOS notification system architecture follows a three-layer pattern: **persistence**, **dispatch**, and **broadcast**. At the foundation, notifications survive as records in the `o_notify` SQLite table. A central `NotifyServer` interface abstracts all operations, while the `MessageBus` handles event publishing and background goroutines manage WebSocket delivery to connected clients.

This design ensures that notifications remain durable across service restarts while allowing the front-end to receive push updates without polling. The system also maintains an in-memory `syncmap.Map` for transient system data that does not require persistence.

## The NotifyServer Interface and Implementation

The `NotifyServer` interface in [`service/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/notify.go) defines the contract for all notification operations. This interface exposes methods for CRUD operations, custom ID-based updates, and specialized dispatch functions.

```go
type NotifyServer interface {
    GetLog(id string) model.AppNotify
    AddLog(log model.AppNotify)
    UpdateLog(log model.AppNotify)
    UpdateLogByCustomID(log model.AppNotify)
    DelLog(id string)
    GetList(c int) (list []model.AppNotify)
    MarkRead(id string, state int)
    SendFileOperateNotify(nowSend bool)
    SendNotify(name string, message map[string]interface{})
    SettingSystemTempData(message map[string]interface{})
    GetSystemTempMap() syncmap.Map
}

```

The concrete implementation (`notifyServer`) maintains two critical dependencies: a GORM database handle for persistent storage and a thread-safe `syncmap.Map` for temporary system data. This structure allows the service to handle both durable notifications and ephemeral status updates within the same unified interface.

## Database Persistence and Models

Notifications persist in the `o_notify` table defined by the `AppNotify` model in [`service/model/o_notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/model/o_notify.go). The GORM model maps directly to database columns and includes fields for `State`, `Message`, `Id`, `Class`, and an optional `CustomId` for external reference tracking.

```go
// Model structure based on source analysis
type AppNotify struct {
    Id        string
    State     int
    Message   string
    Class     int
    CustomId  string
    Type      int
    CreatedAt time.Time
}

```

The `Class` field categorizes notifications (e.g., `types.NOTIFY_APP`), while the `State` field tracks read status using constants like `types.NOTIFY_UNREAD` and `types.NOTIFY_READ`. This schema supports efficient querying for unread notification lists and targeted cleanup operations.

## Message Dispatch and WebSocket Broadcasting

The CasaOS notification system uses a dual-channel delivery mechanism. For event-driven updates, `SendNotify` marshals payload values to JSON strings and publishes them through the central **MessageBus**.

```go
response, err := MyService.MessageBus().
    PublishEventWithResponse(context.Background(),
        common.SERVICENAME, name, msg)

```

This pub/sub layer decouples notification generation from delivery, allowing multiple subscribers to react to system events. Errors and non-200 HTTP responses from the message bus are logged for debugging purposes.

For real-time front-end synchronization, the `SendMeg` background loop (running in [`service/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/notify.go)) pulls pending notifications via `GetList(types.NOTIFY_APP)`, marshals them to JSON, and pushes the payload to every connection stored in `WebSocketConns`. After successful transmission, notifications are automatically marked as read to prevent duplicate delivery.

## Specialized Notification Types

### File Operation Progress

The `SendFileOperateNotify` method handles real-time updates for copy, move, and delete operations. This method periodically scans an in-memory `FileQueue`, builds a `notify.NotifyModel` payload, and publishes it to the message bus under the event name `"casaos:file:operate"`.

```go
go service.MyService.Notify().SendFileOperateNotify(true) // start periodic push

```

Triggered automatically by file handlers in [`route/v1/file.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/route/v1/file.go) (lines 682 and 811), this mechanism provides users with immediate visual feedback on long-running file operations without blocking the main thread.

### System Temporary Data

For non-persistent status information, `SettingSystemTempData` stores arbitrary key/value pairs in the `SystemTempMap`. Other components, such as periodic system-status reporters, read this map via `GetSystemTempMap`. This pattern avoids database writes for rapidly changing metrics like CPU temperature or memory usage.

## Notification Lifecycle Management

The CasaOS notification system follows a complete CRUD lifecycle:

- **Create**: `AddLog` inserts a new `AppNotify` row into the database.
- **Read**: `GetList` queries notifications by class (e.g., `types.NOTIFY_APP`), returning unread or dynamic entries.
- **Update**: `UpdateLog` modifies existing records by ID, while `UpdateLogByCustomID` allows external systems to update notifications using their own reference identifiers.
- **Mark Read**: `MarkRead` updates the `state` column to `types.NOTIFY_READ`.
- **Delete**: `DelLog` removes entries by `custom_id`, typically called during cleanup operations.

```go
// Adding a new notification
notify := model.AppNotify{
    State:    types.NOTIFY_UNREAD,
    Message:  "New backup completed",
    Id:       uuid.NewString(),
    Class:    types.NOTIFY_APP,
    Type:     types.NOTIFY_TYPE_INSTALL_LOG,
}
service.MyService.Notify().AddLog(notify)

// Marking as read
service.MyService.Notify().MarkRead(notificationId, types.NOTIFY_READ)

```

## Integration Examples

Controllers and services interact with the notification system through the `MyService.Notify()` singleton. To send custom events to the front-end:

```go
payload := map[string]interface{}{
    "title": "Upgrade Available",
    "desc":  "Version 2.3.0 is ready to install",
}
service.MyService.Notify().SendNotify("casaos:system:update", payload)

```

The HTTP endpoint in [`route/v1/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/route/v1/notify.go) forwards client POST requests to `Notify().SendNotify`, enabling external triggers and third-party integrations to broadcast messages through the CasaOS notification infrastructure.

## Summary

- **CasaOS notification system** uses a service-oriented architecture with clear separation between storage, dispatch, and delivery layers.
- **Persistence** occurs in SQLite via GORM, using the `o_notify` table and `AppNotify` model defined in [`service/model/o_notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/model/o_notify.go).
- **Real-time delivery** combines MessageBus pub/sub with WebSocket broadcasting managed by the `SendMeg` loop in [`service/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/notify.go).
- **File operations** receive special handling through `SendFileOperateNotify`, which publishes `"casaos:file:operate"` events during copy/move/delete actions.
- **Flexible lifecycle** support includes standard CRUD operations plus `CustomId`-based updates for external system integration.
- **Transient data** storage via `syncmap.Map` handles system metrics without database overhead.

## Frequently Asked Questions

### What database does CasaOS use for notifications?

CasaOS uses **SQLite** as the underlying database for notification persistence, accessed through the **GORM** ORM framework. The `AppNotify` model in [`service/model/o_notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/model/o_notify.go) maps directly to the `o_notify` table, storing fields like `State`, `Message`, `Class`, and `CustomId`.

### How does CasaOS handle real-time notifications?

CasaOS implements real-time notifications through a combination of **MessageBus** pub/sub and **WebSocket** broadcasting. The `SendNotify` method publishes events to the message bus, while the background `SendMeg` loop queries pending notifications and pushes them to all active WebSocket connections stored in `WebSocketConns`, marking them as read upon successful delivery.

### Can third-party apps send notifications through CasaOS?

Yes, third-party applications can send notifications by utilizing the `NotifyServer` interface available through `service.MyService.Notify()`. The HTTP endpoint in [`route/v1/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/route/v1/notify.go) exposes this functionality to external clients, allowing POST requests to trigger `SendNotify` with custom event names and payloads.

### How are file operation progress updates sent?

File operation notifications are handled by the `SendFileOperateNotify` method in [`service/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/notify.go). This method periodically scans the `FileQueue` and publishes `"casaos:file:operate"` events to the MessageBus. File handlers in [`route/v1/file.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/route/v1/file.go) trigger this process during copy, move, or delete operations, ensuring users receive real-time progress updates in the UI.