# CasaOS Notify Service: Architecture, Storage, and Real-Time Event Broadcasting

> Discover the CasaOS notify service architecture. Learn how it stores notifications in SQLite or PostgreSQL and broadcasts real-time events for a seamless user experience.

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

---

**The CasaOS notify service is a centralized event manager implemented in [`service/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/notify.go) that persists notifications to SQLite or PostgreSQL via GORM, broadcasts real-time updates through the Message Bus, and maintains temporary in-memory state for ephemeral system data.**

CasaOS is an open-source home cloud operating system that consolidates file management, app hosting, and system monitoring into a unified interface. At the heart of its event architecture lies the **CasaOS notify service**, a dedicated component that handles everything from application installation alerts to file-operation progress indicators. This service centralizes all user-visible events, manages their persistence in relational databases, and ensures real-time delivery to connected front-end clients through WebSocket connections.

## Core Architecture of the CasaOS Notify Service

The notify service is built around a clean interface-based design that separates concerns between data persistence, real-time messaging, and transient state management.

### The NotifyServer Interface

At the top of [`service/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/notify.go) (lines 25–41), the service declares the `NotifyServer` interface, which defines the contract for notification operations. The concrete `notifyServer` type implements this interface, exposing methods such as `AddLog`, `GetList`, `MarkRead`, `SendNotify`, and `SendFileOperateNotify`. This abstraction allows other CasaOS components to interact with notifications without coupling to specific storage implementations.

### Database Persistence via GORM

The service persists notifications using GORM, an Object-Relational Mapper for Go. The `notifyServer` struct embeds a `*gorm.DB` connection (the `db` field) that interacts with the `model.AppNotify` struct defined in [`service/model/o_notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/model/o_notify.go).

When creating a notification, the `AddLog` method executes `i.db.Create(&log)`, inserting a new row into the database. Updates use `i.db.Save(&log)` for full-record overwrites or targeted `Updates` calls for specific fields like `custom_id`. The `DelLog` method removes entries by `custom_id` (lines 78–81), while `GetList` queries filter records by `class` (notification category) and `state` (read status).

### Real-Time Broadcasting

Beyond persistence, the service broadcasts events to connected clients via the Message Bus. The `SendNotify` method marshals payloads to JSON and invokes `MyService.MessageBus().PublishEventWithResponse` (lines 55–71), distributing messages to WebSocket subscribers. For file operations, `SendFileOperateNotify` constructs a `notify.NotifyModel` and publishes it on the `"casaos:file:operate"` channel (lines 73–118), enabling progress bars and status updates in the UI.

### Temporary In-Memory Storage

Not all system state requires durability. The `notifyServer` maintains a `SystemTempMap` (a `syncmap.Map`) for ephemeral data that disappears on restart. Methods `SettingSystemTempData` and `GetSystemTempMap` provide thread-safe access to this cache (lines 48–53), useful for flags like maintenance mode or transient operation locks.

## How the CasaOS Notify Service Stores Notifications

Understanding the storage layer reveals how CasaOS balances query performance with data integrity.

### The AppNotify Model Structure

Notifications map to the `AppNotify` struct (defined in [`service/model/o_notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/model/o_notify.go)), which includes fields such as `Id`, `CustomId`, `Class`, `State`, `Title`, `Content`, and timestamps. GORM automatically manages table schema migrations based on this struct, eliminating raw SQL manipulation.

### Query Patterns and State Management

The service frequently filters notifications by two columns: `class` and `state`. The `GetList` implementation at lines 44–46 demonstrates a complex query pattern that retrieves active notifications:

```go
i.db.Where("class = ?", c).
    Where(i.db.Where("state = ?", types.NOTIFY_DYNAMICE).Or("state = ?", types.NOTIFY_UNREAD)).
    Find(&list)

```

This query fetches records matching a specific category (`class`) that are either unread (`NOTIFY_UNREAD`) or dynamic (`NOTIFY_DYNAMICE`). The `MarkRead` method updates the `state` column to `NOTIFY_READ` using `i.db.Save(&log)`, transitioning notifications from active to archived status.

## Practical Implementation Examples

The following patterns demonstrate how CasaOS components interact with the notify service in production scenarios.

### Creating and Retrieving Notifications

To log a new event, instantiate an `AppNotify` struct and invoke `AddLog`:

```go
notif := model.AppNotify{
    Class:   types.NOTIFY_APP,
    State:   types.NOTIFY_UNREAD,
    Title:   "Backup started",
    Content: "Backing up /data to Google Drive",
}
MyService.Notify().AddLog(notif)

```

Retrieve unread notifications for a specific category using `GetList`:

```go
unread := MyService.Notify().GetList(types.NOTIFY_APP)
for _, n := range unread {
    fmt.Printf("%s – %s\n", n.Title, n.Content)
}

```

### Managing Notification Lifecycle

Mark specific notifications as read using their ID:

```go
if len(unread) > 0 {
    MyService.Notify().MarkRead(unread[0].Id, types.NOTIFY_READ)
}

```

Delete obsolete notifications by `custom_id` through the `DelLog` method.

### Broadcasting Custom Events

Push real-time alerts to the front-end via the Message Bus:

```go
payload := map[string]interface{}{
    "title":   "System reboot",
    "message": "CasaOS will restart in 1 minute",
}
MyService.Notify().SendNotify("casaos:system:info", payload)

```

### Leveraging Temporary Storage

Store short-lived system flags that persist only until the next restart:

```go
MyService.Notify().SettingSystemTempData(map[string]interface{}{
    "maintenance": true,
})

```

## Key Source Files and Their Responsibilities

- **[`service/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/notify.go)**: Core implementation containing the `NotifyServer` interface, `notifyServer` struct, and all persistence and broadcasting logic (lines 25–118).
- **[`service/model/o_notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/model/o_notify.go)**: GORM model definition for `AppNotify`, specifying database schema and field mappings.
- **[`model/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/model/notify.go)**: Lightweight struct definitions for WebSocket message payloads (`NotifyMssage`).
- **[`types/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/types/notify.go)**: Enumeration constants for notification states (`NOTIFY_UNREAD`, `NOTIFY_READ`, `NOTIFY_DYNAMICE`) and classes.
- **[`common/message.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/common/message.go)**: Service-wide constants such as `SERVICENAME` used when publishing to the Message Bus.

## Summary

- The **CasaOS notify service** centralizes event management through the `NotifyServer` interface implemented in [`service/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/notify.go).
- Notifications persist to SQLite or PostgreSQL via GORM using the `model.AppNotify` struct, with operations abstracted through methods like `AddLog`, `GetList`, and `MarkRead`.
- Real-time delivery occurs through the Message Bus, with `SendNotify` and `SendFileOperateNotify` publishing JSON payloads to WebSocket subscribers on channels such as `"casaos:file:operate"`.
- Ephemeral system state resides in `SystemTempMap`, a thread-safe in-memory cache for data that does not survive restarts.
- The storage layer optimizes queries by filtering on `class` and `state` columns, supporting efficient retrieval of active versus archived notifications.

## Frequently Asked Questions

### What database does CasaOS use for notification storage?

CasaOS supports both SQLite and PostgreSQL backends for the notify service. The implementation in [`service/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/notify.go) uses GORM as the database abstraction layer, allowing the same `model.AppNotify` struct and query logic to work across either database engine without modification to the service code.

### How does CasaOS handle real-time notification updates in the browser?

The service pushes real-time updates via WebSocket connections managed by the Message Bus. When `SendNotify` or `SendFileOperateNotify` is called, the service marshals the notification to JSON and publishes it using `MyService.MessageBus().PublishEventWithResponse`, which broadcasts to all connected front-end clients subscribed to specific channels.

### What is the difference between notification states in CasaOS?

CasaOS defines three primary states in [`types/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/types/notify.go): `NOTIFY_UNREAD` for new alerts requiring user attention, `NOTIFY_READ` for acknowledged notifications, and `NOTIFY_DYNAMICE` for transient progress indicators that update frequently (such as file upload progress). The `GetList` method specifically queries for unread or dynamic notifications to populate active alert panels while excluding archived items.

### Where does CasaOS store temporary notification data that should not persist?

Temporary data resides in `SystemTempMap`, a `syncmap.Map` field within the `notifyServer` struct defined at lines 45–46 of [`service/notify.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/service/notify.go). This in-memory cache stores ephemeral flags and state via `SettingSystemTempData` and `GetSystemTempMap`, ensuring that short-lived operational data (like maintenance mode indicators) automatically clears when the service restarts.