# Entities

The fields every entity has, how entities refer to each other, how they change and what transient entities are.

Entities are the foundation of the Versia protocol. They are similar to [ActivityStreams](https://www.w3.org/TR/activitystreams-core/) objects and [Matrix](https://matrix.org/) events.

## Definition

An entity is a simple JSON object that represents a data structure in Versia, such as a user. Extensions can define their own entity types. Every entity has a `type` and a `created_at`. Every entity also has an `id` that identifies it within its instance, except for [Transient Entities](#transient-entities) and [InstanceMetadata](/entities/instance-metadata).

Objects that do not meet this definition, such as [Media](/structures/media) and [Collection](/structures/collection), are **structures**.

Fields not marked as `required` are [optional](/json#optional-fields).

**Fields**

- `id` (`string`, required): Unique identifier for the entity. **MUST** be unique within the instance, and from 1 to 13 characters long. **MUST** contain only the following characters:

  - `a-z`
  - `0-9`
  - `-`, `_`

  An id is opaque: receivers **MUST NOT** derive a time, an order or any other meaning from it. See [Choosing IDs](#choosing-ids).

  Required on every entity except [Transient Entities](#transient-entities) and [InstanceMetadata](/entities/instance-metadata), which do not have it.

- `type` (`string`, required): Type of the entity. Custom types **MUST** follow [Extension Naming](/extensions#naming).

- `created_at` (`RFC3339`, required): Date and time when the entity was created. **MUST** be an [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339) timestamp.

  > How to handle a well-formed date that is obviously incorrect (e.g. in the future) is left to the Implementation's discretion. Sending a [Note](/extensions/social#note) created on `1337-12-13T00:00:00Z` is very funny, but don't be surprised if it is rejected.

- `updated_at` (`RFC3339`): Date and time when the entity last changed. **MUST** be an [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339) timestamp. See [Changing an Entity](#changing-an-entity).

  Not present on entities without an `id`.

- `extensions` (`Extensions`): Extensions to the entity. Use this to add custom properties to the entity.

  Each key **MUST** follow [extension naming](/extensions#naming).

```jsonc title="Example Entity"
{
    "id": "033gs6adpp000",
    "type": "pub.versia:likes/Like",
    "created_at": "2021-01-01T00:00:00Z",
    "author": "example.com:033wv9k6n0007",
    "target": "example.org:033w19vsgr000"
}
```

```jsonc title="With Extensions"
{
    "id": "033qkb1gq0002",
    "type": "com.example:zlorbs/Zlorb",
    "created_at": "2023-04-13T08:00:00Z",
    "extensions": {
        "com.example:zlorbs": {
            "zlorb_type": "giant",
            "zlorb_size": "huge"
        },
        "com.example:location": {
            "latitude": 37.7749,
            "longitude": -122.4194
        }
    }
}
```

## Choosing IDs

A [reference](#reference) carries the domain of the entity's instance, so an id only has to be unique within that instance, not across the network. So it can be much shorter than a UUID, which at 36 characters is not a valid id. Short ids are also easier to read and type where they appear, such as in [Versia Links](/links) and in the paths of the [Federation API](/federation/endpoints).

How an instance creates ids is up to it. For example, it can take the time the entity is created, in milliseconds since the Unix epoch, multiply it by 65 536, add a counter for entities created in the same millisecond, and write the result in lowercase [Crockford base32](https://www.crockford.com/base32.html), padded with `0` to 13 characters. Crockford base32 leaves out `i`, `l`, `o` and `u`, so an id cannot be misread as `1` or `0` when someone types it.

```text title="An id made from a time and a counter"
0341e147rp002
```

Ids made this way sort by creation time within one instance, which helps the instance store and page through them. Other instances still treat them as opaque.

## Transient Entities

Some entities are **transient**. A Transient Entity has no `id` field, so it cannot be fetched using the [Federation API](/federation/endpoints) or referenced by other entities. These entities are used for actions that do not require a permanent record, such as deletions or migrations.

Implementations do not need to store Transient Entities, and **MUST NOT** rely on other implementations to store them.

## Changing an Entity

To change an entity, its instance sends the whole entity again, with the same `id`, to the [same audience](/federation#delivery). The receiver replaces the copy it holds. An optional field that is left out is cleared.

`id`, `type`, `author` and `created_at` **MUST NOT** change. A receiver **MUST** discard a version that changes any of them. An extension **MAY** lock further fields of its own entities, and states which on its page.

Senders **SHOULD** set `updated_at` when sending a change. A receiver **MUST** discard a version whose `updated_at` is older than that of the copy it holds. Without an `updated_at` to compare, the last version received wins. The same applies to a version it fetched.

## Reference

```typescript
type Id = Entity["id"];

type Reference = `${Domain}:${Id}`;
```

A **reference** is a way to refer to any entity within the Versia network. It is a string composed of the following parts:

- The [domain](#domain) of the entity's instance.
  - Example: `example.com`, `xn--bcher-kva.example`.
- A colon (`:`) separator.
- The entity's unique identifier. This is the `id` property of the entity.

The domain is always present, even when a reference points to an entity on the instance sending it. A string without a domain is not a valid reference. Like every domain, it has no port number.

### Reference Types

A reference does not say what type of entity it points to, but [fetching an entity](/federation/endpoints#entity-data) needs its type. So every field holding a reference **MUST** state the entity type it points to. A field that can point to several types **MUST** carry the type next to the reference, as [Delete](/entities/delete) and [Report](/extensions/reports) do.

### Reference Example

```jsonc
{
    "type": "Subscribe",
    "created_at": "2021-01-01T00:00:00Z",
    "subscriber": "example.com:033wv9k6n0007",
    "target": "example.org:033qtdbdkj006",
    "target_type": "User"
}
```

## Domain

Versia defines a **domain** as the hostname of an instance, which is used to identify the instance in the network. It **MUST** be a valid hostname as defined by [RFC 1123](https://datatracker.ietf.org/doc/html/rfc1123#section-2), and **MUST NOT** include a port number (the HTTPS port is always 443). It **MUST NOT** be an IP address or a single label such as `localhost`. An internationalized domain name **MUST** be written in its ASCII `xn--` form, as defined by [IDNA](https://datatracker.ietf.org/doc/html/rfc5891).

A domain **MUST** be lowercase wherever it appears in the protocol, including [References](/entities#reference), `Versia-Signed-By` and the [signed string](/signatures).

```text title="Valid Domains"
example.com
test.localhost
sus.example.org
xn--bcher-kva.example
1.0.1.7.0.8.0.0.0.0.7.4.0.1.0.0.2.ip6.arpa # We know what you did
```

```text title="Invalid Domains"
example.com:3000   # ❌️ Invalid: port number is not allowed
test..localhost    # ❌️ Invalid: double dot is not allowed
example.com.       # ❌️ Invalid: trailing dot is not allowed
example.org/       # ❌️ Invalid: trailing slash is not allowed
example.org/sus    # ❌️ Invalid: path is not allowed
bücher.example     # ❌️ Invalid: IDNs must be Punycode encoded
Example.com        # ❌️ Invalid: uppercase is not allowed
https://a.example  # ❌️ Invalid: protocol is not allowed
192.0.2.1          # ❌️ Invalid: IP address is not allowed
localhost          # ❌️ Invalid: single label is not allowed
```
