# Meta

Logux stores metadata for every [action] in separated object:

```js
log = [
  [action1, meta1],
  [action2, meta2],
  …
]
```

Meta contains Logux-related data. It should not contain anything related to application state.

Meta has unique action’s ID, creating time, processing status and many other things. Meta is open structure, applications can set custom keys to meta object.

```js
{
  added: 5,
  id: "1564508138460 380:R7BNGAP5:px3-J3oc 0",
  reasons: ['amplifr/lastPrices'],  ​
  subprotocol: 10,
  time: 1564508138460
}
```

[action]: ./action.md

## Setting Meta

Most of methods to add action accept meta as second arguments:

```js
store.dispatch.sync(action, meta)
store.commit.sync(action, meta)
client.log.add(action, meta)
server.log.add(action, meta)
ctx.sendBack(action, meta)
```

The only methods without meta argument:

<details open><summary>Redux client</summary>

`store.dispatch(action)`. Use `store.dispatch.local(action, meta)` to set meta for local-tab actions.

</details>
<details><summary>Vuex client</summary>

`store.commit(action)`. Use `store.commit.local(action, meta)` to set meta for local-tab actions.

</details>

## Changing Meta

In Logux architecture, you can change application state only by adding new action to the log. This is why you can’t change action or action’s order.

Since meta doesn’t contain anything related to application state, you can change meta if it will not affect action’s order.

<details open><summary>Redux client</summary>

```js
store.log.changeMeta(actionId, {
  reasons: []
})
```

</details>
<details><summary>Vuex client</summary>

```js
store.log.changeMeta(actionId, {
  reasons: []
})
```

</details>
<details><summary>Pure JS client</summary>

```js
client.log.changeMeta(actionId, {
  reasons: []
})
```

</details>

You can not change meta’s keys related to action’s order: `id`, `time`, `added`.

On the server you can set `channels`, `users`, `clients` and `nodes` keys (and singular versions) for new action from the client by `resend` callback. If you will return a string or an array of strings, server will set it as `channels`.

<details open><summary>Node.js</summary>

```js
server.type('users/rename', {
  …
  resend (ctx, action, meta) {
    return `users/${ action.userId }`
  },
  …
})
```

</details>
<details><summary>Django</summary>

```python
class RenameUserAction(ActionCommand):

    action_type = 'user/rename'

    def resend(self, action: Action, meta: Optional[Meta]) -> List[str]:
        return [f"users/{action['payload']['userId']}"]
```

</details>
<details><summary>Ruby on Rails</summary>

_Under construction. Until `resend` will be implemented in the gem._

</details>

## Meta Synchronization

Logux synchronizes only 3 meta’s keys:

- `id`
- `time`, but it will be changed to fix time difference between client and server
- `subprotocol`

All other meta keys are local and both server and client do not send them.

## ID and Time

Each action has unique ID. This ID is unique on all machines.

```js
'OzaODN- 380:R7BNGA:1'
```

To generate ID unique across all nodes in Logux cluster, Logux combines 2 values:

- `OzaODN-`: local timestamp on the node, which generate the action.
- `380:R7BNGA:1`: [unique ID] of node, which generate the action.

The timestamp is a number of milliseconds since UNIX epoch encoded to the compact `-0-9A-Z_a-z` alphabet to keep ID short.

The node never repeats the timestamp in own IDs. If the node generates several actions during the same millisecond, the next action will take the next millisecond.

```js
log.generateId() //=> "OzaODN- 380:R7BNGA:1"
log.generateId() //=> "OzaODN0 380:R7BNGA:1"
```

In real world, every node will have own time. For instance, user could set wrong time on own phone. This is why you should not use `meta.id` as a time. Logux has special `meta.time`, which will use time of current node. During the connection client and server will calculate time difference between them and change `meta.time` during synchronization. As result, `meta.time` could be different on different nodes.

`meta.time` is a regular timestamp: milliseconds since UNIX epoch. Only during the burst of actions in the same millisecond Logux could move the action a few milliseconds to the future to keep IDs unique.

```js
const time = new Date(meta.time) //=> Date 2026-08-09T17:35:38.460Z
```

Actions from different nodes can have the same `meta.time`. Logux has `isFirstOlder` helper, which uses both `meta.time` and node ID from `meta.id` to always be sure what action was generated later.

```js
import { isFirstOlder } from '@logux/core'

if (isFirstOlder(meta1, meta2)) {
  lastName = action1.name
} else {
  lastName = action2.name
}
```

If you keep actions in a database, `toSorted()` returns a string to sort with the same order as `isFirstOlder()`. You can put it to a column and sort actions by this column.

```js
import { toSorted } from '@logux/core'

await db.insert({ action, sorted: toSorted(meta) })
//                        sorted: "-OzaODN- 380:R7BNGA:1 -OzaODN-"
```

[unique ID]: ./node.md#node-id

## Common Meta Keys

These meta’s keys are available on client and server:

- `id` string: unique action’s ID.
- `time` timestamp: when action was created. It uses local node’s time.
- `added` number: action’s serial number. Logux uses this number to track what actions were already synchronized.
- `reasons` array of strings: reasons for action to not be cleaned from log. We will cover it in [next chapter].
- `subprotocol` number: [subprotocol] of application, which generates this action.

[next chapter]: ./reason.md
[subprotocol]: ./subprotocol.md

## Client Meta Keys

- `sync` boolean: optional key to mark that this action should be synchronized with other browser tabs and server.
- `tab` string: optional key to mark that action should be visible only for browser tab with the same `client.tabId`.
- `noAutoReason` boolean: optional key to disable setting `timeTravel` reason.

## Server Meta Keys

- `status` `"waiting"|"processed"|"error"`: action processing status.
- `server` string: [node ID] of the server received the action.
- `channels` array and `channel` string: all clients subscribed to listed [channels] will receive the action.
- `users` array and `user` string: all clients with listed user IDs will receive the action.
- `clients` array and `client` string: all clients with listed client IDs will receive the action.
- `nodes` array and `node` string: all clients with listed node IDs will receive the action.

[channels]: ./subscription.md
[node ID]: ./node.md#node-id

[Next chapter](./state.md)
