Skip to content

Commit 95aa0d0

Browse files
authored
Merge pull request #38 from tago-io/tagotip
Added TagoTiP
2 parents 400b94e + 9aed691 commit 95aa0d0

24 files changed

Lines changed: 1741 additions & 82 deletions

.claude/settings.local.json

Lines changed: 0 additions & 36 deletions
This file was deleted.

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ dist/
2828
.env.test.local
2929
.env.production.local
3030

31+
# Generated from tago-io/tagotip GitHub repo (scripts/sync-tagotip.mjs)
32+
docs/tagotip/specification/tagotip-specification.md
33+
docs/tagotip/specification/tagotips-specification.md
34+
3135
npm-debug.log*
3236
yarn-debug.log*
3337
yarn-error.log*

docs/tagoio/devices/sending-data.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ When making a request to the TagoIO API, you must also specify the appropriate r
99

1010
:::
1111

12+
:::tip Looking for a lighter protocol?
13+
[TagoTiP](/docs/tagotip/) lets you send the same data in ~130 bytes instead of ~487 - no JSON, no HTTP headers. Ideal for constrained devices. See [TagoTiP over HTTP](/docs/tagotip/transports/http) for the HTTP transport.
14+
:::
15+
1216
A device can send data to TagoIO by using the POST method.
1317

1418
```

docs/tagotip/introduction.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
---
2+
sidebar_position: 1
3+
sidebar_label: Introduction
4+
title: TagoTiP - Transport IoT Protocol
5+
slug: /tagotip/
6+
---
7+
8+
# TagoTiP - Transport IoT Protocol
9+
10+
Send IoT data to TagoIO in **130 bytes** instead of 487. No JSON, no HTTP headers - just a single human-readable line your microcontroller can build with `sprintf`.
11+
12+
```
13+
PUSH|4deedd7bab8817ec|sensor-01|[temperature:=32.5#C;humidity:=65#%]
14+
```
15+
16+
## Why TagoTiP?
17+
18+
| | HTTP/JSON | TagoTiP | TagoTiP(s) (encrypted) |
19+
|---|---|---|---|
20+
| **Payload size** | ~487 bytes | ~130 bytes | ~119 bytes |
21+
| **vs. HTTP/JSON** | - | 3.7x smaller | 4.1x smaller |
22+
| **TLS required?** | Yes | Recommended | No - AEAD encryption built-in |
23+
| **Parse complexity** | JSON parser | Linear scan, no backtracking | Envelope + linear scan |
24+
25+
### Built for constrained devices
26+
27+
- **Human-readable** - debug frames in a terminal, compose them by hand
28+
- **Type-safe** - explicit operators for numbers (`:=`), strings (`=`), booleans (`?=`), and locations (`@=`)
29+
- **C-friendly** - predictable buffer sizes, no dynamic allocation, linear parsing
30+
- **Compact** - variable, value, unit, timestamp, group, location, and metadata in a single frame
31+
- **Transport-agnostic** - works over UDP, TCP, HTTP(S), MQTT, or any byte-capable channel
32+
33+
## Encryption without TLS
34+
35+
Need security on raw UDP or constrained links where TLS is too expensive? **TagoTiP(s)** wraps frames in an AEAD authenticated encryption envelope - as little as **29 bytes** of overhead, with built-in replay protection and integrity verification.
36+
37+
| Cipher Suite | Key | Tag | Envelope Overhead |
38+
|---|---|---|---|
39+
| **AES-128-CCM** | 128-bit | 8 B | 29 bytes |
40+
| AES-128-GCM | 128-bit | 16 B | 37 bytes |
41+
| AES-256-CCM | 256-bit | 8 B | 29 bytes |
42+
| AES-256-GCM | 256-bit | 16 B | 37 bytes |
43+
| ChaCha20-Poly1305 | 256-bit | 16 B | 37 bytes |
44+
45+
Learn more in the [Encryption](./specification/encryption) guide.
46+
47+
## How it compares
48+
49+
### TagoTiP vs. other IoT data formats
50+
51+
| | TagoTiP | HTTP + JSON | MQTT + JSON | Protobuf |
52+
|---|---|---|---|---|
53+
| **Typical payload** | ~130 bytes | ~487 bytes | ~210 bytes | ~80 bytes |
54+
| **Human-readable** | Yes | Partially | Partially | No |
55+
| **Schema required** | No | No | No | Yes |
56+
| **Debug in a terminal** | Yes | Verbose | Binary framing | No |
57+
| **Build with `sprintf`** | Yes | Complex | Needs MQTT library | Needs code generator |
58+
| **IoT type system** | Built-in (number, string, bool, location) | Application-defined | Application-defined | Schema-defined |
59+
| **Metadata, unit, group, timestamp** | Native syntax | Application-defined | Application-defined | Schema-defined |
60+
61+
### TagoTiP(s) vs. other IoT security
62+
63+
| | TagoTiP(s) | TLS 1.3 | DTLS 1.2 |
64+
|---|---|---|---|
65+
| **Handshake** | None - 0 bytes | ~2-4 KB | ~2-5 KB |
66+
| **Round trips before first data** | 0 | 1-2 | 2-3 |
67+
| **Per-message overhead** | 29-37 bytes | ~29 bytes + TCP | ~29 bytes |
68+
| **Session state** | Stateless | Per-connection | Per-connection |
69+
| **Certificate management** | None | Required | Required or PSK |
70+
| **Works over raw UDP** | Yes | No | UDP only |
71+
| **Works without TCP** | Yes | No | UDP only |
72+
73+
## Quick example
74+
75+
Push a temperature reading with unit, timestamp, and metadata - all in one frame:
76+
77+
```
78+
PUSH|4deedd7bab8817ec|sensor-01|@1694567890000^batch_42{firmware=2.1}[temperature:=32.5#C;position@=39.74,-104.99]
79+
```
80+
81+
Pull the last value back:
82+
83+
```
84+
PULL|4deedd7bab8817ec|sensor-01|[temperature]
85+
<- ACK|OK|[temperature:=32.5#C@1694567890000]
86+
```
87+
88+
## SDKs
89+
90+
All language SDKs share a single Rust core (`tagotip-codec`, `no_std`), so parsing and frame building behave identically everywhere.
91+
92+
| Package | Language | Install |
93+
|---------|----------|---------|
94+
| [`@tagoio/tagotip`](https://github.com/tago-io/tagotip-sdk/tree/main/tagotip-node) | TypeScript / Node.js | `npm install @tagoio/tagotip` |
95+
| [`tagotip`](https://github.com/tago-io/tagotip-sdk/tree/main/tagotip-go) | Go | `go get github.com/tago-io/tagotip-sdk/tagotip-go` |
96+
| [`tagotip`](https://github.com/tago-io/tagotip-sdk/tree/main/tagotip-python) | Python | `pip install tagotip` |
97+
| [`TagoTiP`](https://github.com/tago-io/tagotip-sdk/tree/main/tagotip-arduino) | C / Arduino | Arduino Library Manager |
98+
| [`tagotip-codec`](https://github.com/tago-io/tagotip-sdk/tree/main/tagotip-codec) | Rust | `cargo add tagotip-codec` |
99+
100+
Browse the full SDK source at [github.com/tago-io/tagotip-sdk](https://github.com/tago-io/tagotip-sdk).
101+
102+
## Open source
103+
104+
TagoTiP is open source under the [Apache License 2.0](https://github.com/tago-io/tagotip). Implement clients, servers, libraries, or gateways - for any purpose, including commercial use.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"label": "Getting Started",
3+
"position": 2,
4+
"collapsed": true
5+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
sidebar_position: 3
3+
sidebar_label: Choosing a Transport
4+
title: Choosing a Transport
5+
---
6+
7+
# Choosing a Transport
8+
9+
TagoTiP is transport-agnostic - the same protocol frames work over any transport. Pick the one that fits your hardware and use case.
10+
11+
<div class="transport-list">
12+
<a class="transport-card" href="../transports/udp">
13+
<h3>UDP</h3>
14+
<p>Battery-powered sensors that wake up, send a reading, and sleep. Zero connection overhead - one datagram in, one datagram out. Every milliamp counts.</p>
15+
<div class="transport-card__details">
16+
<span>No connection</span>
17+
<span>Server push via PING poll</span>
18+
<span><a href="../specification/encryption">TagoTiP(s)</a> on port <code>5684</code></span>
19+
</div>
20+
</a>
21+
<a class="transport-card" href="../transports/tcp">
22+
<h3>TCP</h3>
23+
<p>Always-on gateways that stream data and need commands instantly - reboots, config updates, OTA triggers. Guaranteed, ordered delivery over a persistent connection.</p>
24+
<div class="transport-card__details">
25+
<span>Persistent connection</span>
26+
<span>Instant CMD push</span>
27+
<span>TLS on port <code>5694</code></span>
28+
</div>
29+
</a>
30+
<a class="transport-card" href="../transports/http">
31+
<h3>HTTP</h3>
32+
<p>Cloud integrations, serverless functions, or any device behind firewalls and proxies. Standard POST/GET/HEAD with a single Authorization header. No special libraries needed.</p>
33+
<div class="transport-card__details">
34+
<span>Per-request</span>
35+
<span>Server push via HEAD poll</span>
36+
<span>HTTPS on port <code>443</code></span>
37+
</div>
38+
</a>
39+
<a class="transport-card" href="../transports/mqtt">
40+
<h3>MQTT</h3>
41+
<p>Large device fleets on unreliable networks. Publish/subscribe with QoS levels 0, 1, and 2. Topic-based routing with native keepalive and reconnection handling.</p>
42+
<div class="transport-card__details">
43+
<span>Persistent connection</span>
44+
<span>Instant via ack topic</span>
45+
<span>TLS on port <code>8883</code></span>
46+
</div>
47+
</a>
48+
</div>

docs/tagotip/servers/endpoints.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
sidebar_position: 2
3+
sidebar_label: Endpoints
4+
title: Endpoints
5+
---
6+
7+
# Endpoints
8+
9+
TagoTiP servers are region-specific. Each transport protocol has its own dedicated hostname and static IP address. Connect to the region closest to your devices for the lowest latency.
10+
11+
## US-East-1
12+
13+
| Transport | Hostname | Port | Protocol | Security |
14+
|-----------|----------|------|----------|----------|
15+
| UDP | `udp.tip.us-e1.tago.io`<br/><span class="ip-addr">166.117.107.129</span> | 5683 | TagoTiP | None |
16+
| UDP | `udp.tip.us-e1.tago.io`<br/><span class="ip-addr">166.117.107.129</span> | 5684 | TagoTiP(s) | Encrypted (AEAD) |
17+
| TCP | `tcp.tip.us-e1.tago.io`<br/><span class="ip-addr">75.2.126.170</span> | 5693 | TagoTiP(s) | None |
18+
| TCP | `tcp.tip.us-e1.tago.io`<br/><span class="ip-addr">75.2.126.170</span> | 5694 | TagoTiP(s) | TLS |
19+
| HTTP | `http.tip.us-e1.tago.io`<br/><span class="ip-addr">52.223.14.189</span> | 80 | TagoTiP(s) | None (HTTP) |
20+
| HTTP | `http.tip.us-e1.tago.io`<br/><span class="ip-addr">52.223.14.189</span> | 443 | TagoTiP(s) | TLS (HTTPS) |
21+
| MQTT | `mqtt.tip.us-e1.tago.io`<br/><span class="ip-addr">15.197.247.146</span> | 1883 | TagoTiP | None (MQTT) |
22+
| MQTT | `mqtt.tip.us-e1.tago.io`<br/><span class="ip-addr">15.197.247.146</span> | 8883 | TagoTiP | TLS (MQTTS) |
23+
24+
## EU-West-1
25+
26+
| Transport | Hostname | Port | Protocol | Security |
27+
|-----------|----------|------|----------|----------|
28+
| UDP | `udp.tip.eu-w1.tago.io`<br/><span class="ip-addr">166.117.51.137</span> | 5683 | TagoTiP | None |
29+
| UDP | `udp.tip.eu-w1.tago.io`<br/><span class="ip-addr">166.117.51.137</span> | 5684 | TagoTiP(s) | Encrypted (AEAD) |
30+
| TCP | `tcp.tip.eu-w1.tago.io`<br/><span class="ip-addr">15.197.224.153</span> | 5693 | TagoTiP(s) | None |
31+
| TCP | `tcp.tip.eu-w1.tago.io`<br/><span class="ip-addr">15.197.224.153</span> | 5694 | TagoTiP(s) | TLS |
32+
| HTTP | `http.tip.eu-w1.tago.io`<br/><span class="ip-addr">166.117.2.140</span> | 80 | TagoTiP(s) | None (HTTP) |
33+
| HTTP | `http.tip.eu-w1.tago.io`<br/><span class="ip-addr">166.117.2.140</span> | 443 | TagoTiP(s) | TLS (HTTPS) |
34+
| MQTT | `mqtt.tip.eu-w1.tago.io`<br/><span class="ip-addr">166.117.88.178</span> | 1883 | TagoTiP | None (MQTT) |
35+
| MQTT | `mqtt.tip.eu-w1.tago.io`<br/><span class="ip-addr">166.117.88.178</span> | 8883 | TagoTiP | TLS (MQTTS) |
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
sidebar_position: 4
3+
sidebar_label: Rate Limits
4+
title: Rate Limits
5+
---
6+
7+
# Rate Limits
8+
9+
Limits are enforced at two levels: per [profile](/docs/tagoio/profiles/) and per device. Defaults vary by plan. Exceeding a limit returns `ACK|ERR|rate_limited` (or HTTP `429`).
10+
11+
RPM = requests per minute.
12+
13+
## Per profile
14+
15+
| Resource | Transports | Scale | Starter | Free |
16+
|---|---|---|---|---|
17+
| Uplink RPM (PUSH) | UDP, TCP, HTTP, MQTT | 1,000 | 500 | 60 |
18+
| Downlink RPM (PULL) | UDP, TCP, HTTP, MQTT | 1,000 | 500 | 60 |
19+
| Connections per IP | TCP, HTTP, MQTT | 100 | 20 | 5 |
20+
21+
## Per device
22+
23+
| Resource | Transports | Default |
24+
|---|---|---|
25+
| Max payload size | UDP, TCP, HTTP, MQTT | 100 KB |
26+
| Connection TTL | TCP, MQTT | 60 s |
27+
| Keep-alive idle timeout | TCP, MQTT | 20 s |
28+
29+
PING is exempt from rate limiting on TCP and UDP. On HTTP, `HEAD` counts toward the uplink RPM. On MQTT, keepalive is handled natively by PINGREQ/PINGRESP.
30+
31+
See each transport page for transport-specific limits.

docs/tagotip/servers/setup.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
sidebar_position: 1
3+
sidebar_label: Quick Start
4+
title: Quick Start
5+
---
6+
7+
# Quick Start
8+
9+
Set up your device and authorization before sending data over any transport.
10+
11+
## Step 1: Create a Device
12+
13+
1. Go to **Devices** in your TagoIO account and create a new device.
14+
2. Select the network **TagoTiP** and the connector for your transport (UDP, TCP, HTTP, or MQTT).
15+
3. Assign a **Serial Number** (e.g., `SN0042`) - this is how TagoTiP identifies your device.
16+
17+
## Step 2: Create an Authorization
18+
19+
1. Go to **Devices** > **Authorization** at the top of the page.
20+
2. Click **Generate**.
21+
3. Set a name and select the token format **TagoTiP(s)**.
22+
4. Click **Generate**.
23+
24+
You receive two credentials:
25+
26+
| Credential | Example | Used for |
27+
|---|---|---|
28+
| **Token Hash** | `4deedd7bab8817ec` (16 hex chars) | TagoTiP frames (plaintext) |
29+
| **Authorization** | `ate2bd...c0d0` (34 chars) | [TagoTiP(s)](../specification/encryption) encryption key derivation |
30+
31+
:::tip
32+
The token hash is safe on the wire - it cannot be reversed to reveal the authorization. Keep the authorization (`at...`) secret; it is only needed for TagoTiP(s) key derivation.
33+
:::
34+
35+
See the [Authorization guide](/docs/tagoio/integrations/general/authorization) for more details.
36+
37+
## Next steps
38+
39+
Now [choose a transport](./choosing-a-transport) and send your first data point.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"label": "Specification",
3+
"position": 4,
4+
"collapsed": true
5+
}

0 commit comments

Comments
 (0)