> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ilyama.golain.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Point dataspaces

> Append-only Influx-style sparse measurements — ClickHouse MergeTree with a uniform JSON map, ready for cold storage from create.

A **point** dataspace is append-only history like [telemetry](/dataspaces/timeseries), but each sample is a **sparse bag of metric names** rather than a fixed column list.

Think Influx line protocol: `temperature=28.9,rssi=-70,status=online` at one timestamp. The next sample from another device might omit `rssi` and add `battery`. You do not declare those keys as schema up front.

The API modality is **`point`**.

→ [All modalities](/dataspaces/overview)

## When to use it

**Use point when**

* Firmware emits **different measurement keys** per device, version, or vendor.
* You do not want a promotion ladder or a frozen column list.
* You will extract keys at query time (`temperature`, `rssi`, …) rather than treat each metric as a first-class catalog field.

**Use [telemetry](/dataspaces/timeseries) instead** when you know the column set (or will freeze one) and want typed columns for dashboards, joins, and `hot_cold` after promotion.

**Do not use point** for current configuration, shadows, or anything you will update. It is append-only. Use [state](/dataspaces/state) or [mutable](/dataspaces/mutable).

Point is a **cloud create** choice. Edge sync attach maps `telemetry` / `timeseries` to a **telemetry** dataspace, not point. Create a point dataspace yourself if you want this shape.

## Backing store

Point lives in **ClickHouse** as a `MergeTree`, same append family as telemetry.

| Physical detail   | Behavior                                                   |
| ----------------- | ---------------------------------------------------------- |
| Table engine      | `MergeTree` — append only                                  |
| Sort key          | `(source_id, event_ts)`                                    |
| Partitioning      | By month of `event_ts`                                     |
| Layout            | **`map` only** — one uniform JSON measurements object      |
| Updates / deletes | Not supported on the hot path                              |
| Dedup on retry    | Native block deduplication for an identical replayed batch |

The measurements column is **JSON**, not a native map type. That is what makes **Parquet cold storage** possible. A ClickHouse `Map` of dynamic values cannot round-trip to Parquet; JSON can.

## Why `map` does not promote

Telemetry climbs `jsonb` → `hybrid` → `typed` because named fields become columns. Point **starts uniform** and stays uniform: every new key is another entry in the same JSON object. There is no per-key catalog promotion.

That is also why point can take **`tiering: hot_cold` immediately**. The layout is already schema-stable. Telemetry must freeze to `typed` first.

Surprise keys never orphan a point batch — they join the measurements object. Typed-layout orphaning does not apply.

## Meta columns

Same as telemetry:

| Column        | Meaning                       |
| ------------- | ----------------------------- |
| `source_type` | Producer class                |
| `source_id`   | Producer UUID                 |
| `event_ts`    | Event time (UTC, millisecond) |
| `ingest_ts`   | Server receive time           |

User measurements live in the map, not as sibling typed columns (unless you later expose extracted keys through a read-view projection).

## Time window, tiering, storage class

Same ingress rules as telemetry:

* `event_ts` more than one day in the future → **dropped-rows** (`future`).
* Late history → dropped only when **`hot_cold`** is on.
* Hot-only point dataspaces accept old backfill.

| Setting                | Point-specific note                                                                                                  |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `storage_class: fast`  | Default hot MergeTree                                                                                                |
| `storage_class: cheap` | Parquet-on-object-storage with no hot table. Point is the modality this is designed for: uniform layout from create. |
| `tiering: hot_cold`    | Allowed from create. Cold is immutable; late writes that miss the hot window are diverted.                           |

## Events, APIs, and edge

* **No per-insert events.** Query the dataspace.
* **No HTTP row CRUD.** There is no current-row identity.
* **No `edge_sqlite` sync target.** Upsync-only, same as telemetry. Attaching bidirectional sync is rejected.

Reads: QueryScript uses the dataspace **name**. Raw SQL uses the catalog `read_view`. Extract keys from the measurements JSON in the query (the read gateway can push down filters and aggregates on projected keys).

## Create

```bash theme={null}
curl -sS -X POST "$API/projects/$PROJECT_ID/dataspaces" \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "vendor_metrics",
    "modality": "point",
    "tiering": "hot_cold"
  }'
```

Cheaper storage, no hot table (queries hit Parquet):

```bash theme={null}
curl -sS -X POST "$API/projects/$PROJECT_ID/dataspaces" \
  -H "Authorization: Bearer $TOKEN" \
  -H "ORG-ID: $ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "archive_metrics",
    "modality": "point",
    "storage_class": "cheap"
  }'
```

## Related

* [Dataspaces overview](/dataspaces/overview)
* [Schema and layouts](/dataspaces/schema)
* [Storage](/dataspaces/storage)
* [Telemetry](/dataspaces/timeseries) — named columns and promotion
* [API introduction](/api-reference/introduction)
