From first table to first query.
The database console is the starting point for application data. Sign in, choose an organization, then open Databases. If you are new, create an organization first.
Databases opens with a searchable list. Filter by status, choose 25 or 50 rows per page, then select Open database. Inside a database, choose a table in the left panel (or the table selector on mobile). Use All databases to return to the list.
Create a database and table
Give the database a name, then create a table called
readings. Keep the requiredrow_idstring andtimetimestamp columns. Addsensoras a string andtemperatureas float64.Issue a database key
In the database’s API keys panel, issue a key with
readandwritegrants. Copy the secret when it is shown, then use it in the console connection. Ask an organization owner or admin if you cannot manage keys.Insert your first rows
Open SQL editor and select Insert JSON. Paste the sample rows below and run the insert. The sample timestamps are fixed examples; use observation times from your application for new data.
Read the result
Switch the editor to SELECT and run the query in Query with SQL. A successful insert returns a commit receipt; the SELECT result lets you check the stored values.
[
{
"row_id": "reading-001",
"time": "2026-10-26T00:00:00.000Z",
"sensor": "greenhouse",
"temperature": 25.31
},
{
"row_id": "reading-002",
"time": "2026-10-26T00:01:00.000Z",
"sensor": "greenhouse",
"temperature": 25.48
}
]Use UTC ISO 8601 timestamps. Encode int64, uint64, and decimal128 values as strings to preserve precision. The sample uses float64 for temperature.
Choose a database connection.
Open your database’s Connections panel. HTTPS is available for inserts, SQL, and supported mutations. MQTT is coming soon as another protocol for the same database and tables. Creating a device or datastream is not required.
HTTPS · Available
Use the exact database URL from Connections and a key scoped to that database. Replace the placeholders with your own database ID and key. Keep the key in your server-side environment.
Secret keys are shown once. If you lose one, create or rotate a key, update your application, then revoke the old key when it is no longer used. Viewers should request a replacement from an administrator.
export CHRONOUS_DATABASE_URL="https://kunanta-telemetry.yoga-pratama-pangestu.workers.dev/v1/databases/<database-id>"
export CHRONOUS_DATABASE_KEY="<database-key>"
curl "$CHRONOUS_DATABASE_URL/connection" \
-H "Authorization: Bearer $CHRONOUS_DATABASE_KEY"To query from a server-side JavaScript application, copy the table ID from the selected table and provide it with the SQL statement:
const response = await fetch(
process.env.CHRONOUS_DATABASE_URL + "/query",
{
method: "POST",
headers: {
authorization: "Bearer " + process.env.CHRONOUS_DATABASE_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
tableId: "<table-id>",
sql: 'SELECT * FROM "readings" LIMIT 100',
parameters: [],
}),
},
);
const result = await response.json();
if (!response.ok) throw new Error(JSON.stringify(result.error));
console.log(result);Console login and database keys have different jobs. Your account selects and manages resources; a database key authorizes data operations. The current connection interface is HTTPS, rather than PostgreSQL or MySQL wire protocol.
MQTT database connections Coming soon
MQTT is planned as a connection protocol for database tables, alongside HTTPS. The target is the same schema validation, database permissions, and durable commit receipts.
This release does not provide a database MQTT endpoint, topic contract, or MQTT credential setup. Use HTTPS from Database → Connections today.
Query with familiar SQL.
The editor has three modes. Choose a mode, write the request in the editor, then select Run or Insert rows. Ctrl/Cmd + Enter runs the current mode. Filters, grouping, and aggregate functions are written inside a SELECT statement.
| Mode | Use it for |
|---|---|
SELECT | Read, filter, group, and sort one table. Requires a read key grant. |
UPDATE / DELETE | Change values or logically delete rows. Requires write and an explicit WHERE clause. |
| Insert JSON | Add rows as a JSON array. Requires write. SQL INSERT INTO is not supported. |
Choose the table in the console and connect a database key first. Replace readings and column names in these examples with your actual schema. The quickstart table has row_id, time, sensor (string), and temperature (float64).
Read columns and sort rows
SELECT "time", "sensor", "temperature"
FROM "readings"
ORDER BY "time" DESC, "row_id"
LIMIT 100Use SELECT * for all columns or list the columns you need. Column and table aliases use AS. Double quotes preserve identifier spelling; unquoted names are normalized to lowercase. Use single quotes for string values. The table name must match the selected table; cross-database names are unavailable.
Filter before aggregating
SELECT "row_id", "time", "temperature"
FROM "readings"
WHERE "sensor" IN ('greenhouse', 'nursery')
AND "temperature" BETWEEN 20 AND 30
AND "temperature" IS NOT NULL
ORDER BY "temperature" DESC NULLS LAST, "row_id"
LIMIT 50 OFFSET 0| Syntax | Behavior |
|---|---|
WHERE | Compare with =, <> (or !=), >, <, >=, and <=. Combine conditions with AND, OR, NOT, and parentheses. |
BETWEEN / IN | BETWEEN includes both endpoints. IN accepts up to 64 values. NOT BETWEEN and NOT IN are also accepted. |
IS NULL / IS NOT NULL | Test missing values. column = NULL does not select null values. Zero, false, an empty string, and null are different values. |
GROUP BY / HAVING | Group rows, then filter aggregate results with HAVING. Up to eight grouping expressions. |
ORDER BY | Up to eight expressions, with ASC/DESC and NULLS FIRST/NULLS LAST. Include a unique tie-breaker such as row_id for stable ordering. |
LIMIT / OFFSET | LIMIT up to 1,000; OFFSET up to 2,400. Without ORDER BY, row order is not guaranteed. Large OFFSET values still consume scan budget. |
Aggregate by sensor
SELECT "sensor", COUNT(*) AS "readings",
AVG("temperature") AS "average_temperature"
FROM "readings"
GROUP BY "sensor"
HAVING COUNT(*) > 0
ORDER BY "sensor"
LIMIT 100| Function | Supported input and result |
|---|---|
COUNT(*) / COUNT(column) | Count all rows or only non-null column values. Returns int64, encoded as a JSON string. |
MIN(column) / MAX(column) | Return the smallest/largest non-null value, preserving the column type and decimal precision/scale. |
SUM(column) | Numeric columns only. int32 → int64; int64/uint64 → decimal128(38,0); decimal128(p,s) → decimal128(38,s); float64 → float64. Integer/decimal results are exact strings; overflow fails the query. |
AVG(column) | float64 only, returning float64. Integer and decimal averages are not supported. Float results follow floating point arithmetic. |
COUNT returns zero for empty input. SUM, AVG, MIN, and MAX ignore nulls and return null for empty or all-null input. Grouping only returns groups present in the selected data; missing groups are not filled with zero. Aggregate arguments are a single column, except COUNT(*).
Group timestamps into UTC buckets
SELECT DATE_TRUNC('hour', "time") AS "bucket_start",
COUNT(*) AS "readings",
AVG("temperature") AS "average_temperature"
FROM "readings"
GROUP BY DATE_TRUNC('hour', "time")
ORDER BY "bucket_start"
LIMIT 100DATE_TRUNC accepts 'minute', 'hour', or 'day' and a timestamp_ms column. Buckets use UTC, with no local timezone conversion, month/week buckets, gap filling, or resampling. Add a time filter for larger tables.
Count true boolean values
If your table has a boolean column named healthy, this form counts true values while excluding false and null:
SELECT COUNT(*) AS "total",
COUNT(*) FILTER (WHERE "healthy" = TRUE) AS "healthy_count"
FROM "readings"FILTER is restricted to COUNT with boolean_column = TRUE. Other FILTER predicates and FILTER on SUM/AVG/MIN/MAX are not supported.
Use typed parameters.
Place $1, $2, and so on in SQL, then open JSON parameters below the editor. Paste a JSON array in the same order. Values are bound separately from the SQL text.
SELECT "row_id", "time", "temperature"
FROM "readings"
WHERE "time" >= $1 AND "time" < $2
AND "sensor" = $3
ORDER BY "time", "row_id"
LIMIT 100[
{
"type": "timestamp_ms",
"value": "2026-10-26T00:00:00.000Z"
},
{
"type": "timestamp_ms",
"value": "2026-10-27T00:00:00.000Z"
},
{
"type": "string",
"value": "greenhouse"
}
]This range includes its start and excludes its end. Parameters must be consecutive from $1 to $16, with every supplied position used. Named parameters, $01, missing values, and extra values are rejected. Parameters represent values, not table or column names. Use [] when there are no placeholders.
| Type | JSON value |
|---|---|
int32 | Integer number, for example 123. |
int64 / uint64 | Integer string, for example "9223372036854775807". uint64 cannot be negative. |
float64 | Finite JSON number, for example 25.31. NaN and Infinity are invalid. |
decimal128 | Fixed-scale string, for example "1234.5600". Parameters also need precision and scale matching the column. |
boolean / string | JSON true/false or a string such as "greenhouse". |
timestamp_ms | UTC ISO 8601 string with millisecond precision, for example "2026-10-26T00:00:00.000Z". |
null | Use "value": null with the actual parameter type. Decimal nulls still require precision/scale. Use IS NULL in SQL to match null values. |
[
{
"type": "decimal128",
"precision": 20,
"scale": 4,
"value": "1234.5600"
}
]Use parameters of the matching type for int64, uint64, decimal, and timestamp comparisons, including HAVING over exact aggregates. Decimal precision and scale must match the value being compared. Large numbers must not pass through JavaScript Number. Query results also encode int64, uint64, and decimal values as strings.
Insert rows with JSON.
Select Insert JSON, connect a key with write, and paste an array of 1–600 row objects. This mode takes the array directly; the console supplies the schema revision and request identity. Use the selected table’s current schema.
[
{
"row_id": "reading-003",
"time": "2026-10-26T00:02:00.000Z",
"sensor": "greenhouse",
"temperature": 25.62
}
]Every row needs a unique row_id string and a time timestamp. Supply all required columns; omitted nullable columns become null. Row values use the encoding above without parameter wrappers. Use your actual observation times for new data; these timestamps are fixed examples.
| Situation | Result |
|---|---|
| New row_id | Inserts a row and returns a commit receipt with inserted and duplicate counts. |
| Same row_id and identical content | Recognized as an exact duplicate; no additional row is inserted. |
| Same row_id with different content | ROW_CONFLICT. Use UPDATE to correct an existing row. |
| Invalid row or schema conflict | The entire batch fails. There is no partial successful insert. |
| Lost response | Retry the original operation with the same request identity and payload. The durable receipt prevents the same request from being executed twice. |
The default batch budget is 600 rows and 256 KiB of canonical row data, with up to 32 KiB per row. A batch can hit the byte limit before the row limit. Check the connection limits for your database.
Update or delete matching rows.
Choose UPDATE / DELETE and connect a key with write. Run one statement at a time. Both operations require a WHERE clause and target the selected table. First use SELECT with the same filter to review which rows will change.
UPDATE "readings"
SET "temperature" = $1
WHERE "row_id" = $2[
{
"type": "float64",
"value": 25.75
},
{
"type": "string",
"value": "reading-001"
}
]UPDATE preserves columns that are not assigned. Assignments support scalar values, typed parameters, and compatible column references. You can assign multiple columns separated by commas. General arithmetic such as temperature + 1 is unavailable. row_id and time are immutable.
UPDATE "readings"
SET "temperature" = NULL
WHERE "row_id" = 'reading-002'SET NULL only works for a nullable column. Use an empty parameter array for this example.
DELETE FROM "readings"
WHERE "row_id" = $1[
{
"type": "string",
"value": "reading-003"
}
]DELETE hides matching rows from later reads and records a tombstone; it does not immediately erase stored objects. A deleted row_id cannot be reused. Successful changes return a receipt with affected row count, row revisions, schema revision, and commit token.
Mutations are atomic and limited to 600 affected rows per statement, subject to the scan and resource budgets. A limit or conflict fails the operation rather than silently changing only part of the selection. Joins, extra FROM tables, RETURNING, and multiple statements are unavailable.
Use the same operations over HTTPS
| Endpoint | Request body and grant |
|---|---|
POST /query | {tableId, sql, parameters}; read grant. Optional timeoutMs and atLeastCommit. |
POST /tables/{tableId}/rows | {issuedAt, schemaRevision, rows}; write grant. |
POST /tables/{tableId}/mutations | {issuedAt, schemaRevision, operation: {op: "sql", sql, parameters}}; write grant. |
Use the database URL from Connections, bearer authorization, and JSON content type. For writes, generate issuedAt at submission time as UTC ISO 8601 with milliseconds and an Idempotency-Key of <epochMs>.<nonce>. The epoch must equal issuedAt and the nonce is 16 random bytes encoded as unpadded base64url. Retain the original body and key for retries; the retry window is seven days. SELECT does not require an idempotency key.
Limits, results, and errors.
Each query reads one committed snapshot of one table. Concurrent writes appear in a later query. Results include column types, rows, scan counts, schema revision, and a snapshot token. A successful query returns complete results within the requested SQL and budgets; resource failures return an error without partial results.
| Budget | Default maximum |
|---|---|
| Statement and parameters | One statement; 8 KiB SQL; 16 parameters; 32 KiB query request body. |
| Projection and grouping | 32 output columns; eight GROUP BY and eight ORDER BY expressions; 64 IN values. |
| Scan | 2,400 rows / 8 MiB. Narrow time filters can reduce selected data. LIMIT restricts the output, not the work needed to compute it. |
| Result | 1,000 rows / 1 MiB serialized response, including metadata. |
| Execution | 15 seconds by default and at most; API timeoutMs may shorten this. DataFusion memory pool: 64 MiB, without disk spill. |
These are execution budgets, separate from storage capacity and plan allowances. Database connection metadata reports the active limits. atLeastCommit on the API accepts a receipt token from the same table as a minimum visibility check; it does not select a historical snapshot. The console carries the latest successful write token into subsequent SELECT requests.
SQL outside the supported subset
JOIN, subqueries, WITH/CTE, UNION/set operations, DISTINCT, window/OVER functions, nested aggregates, CASE, CAST, LIKE/ILIKE, general arithmetic, and user-defined functions are unavailable. FILTER only supports the COUNT form above. SQL INSERT, CREATE, ALTER, DROP, EXPLAIN, COPY, and SET are unavailable. Use the console’s table/schema controls for supported schema changes. Queries cannot access files, URLs, external tables, or information_schema.
| Error | What to check |
|---|---|
UNSUPPORTED_SQL | Use one supported statement for one table. Check functions, clauses, and parameter types. |
INVALID_REQUEST | Check JSON, placeholders, timestamp formatting, column types, and decimal precision/scale. |
UNAUTHENTICATED / FORBIDDEN | Reconnect a valid database key with the required read or write grant. Admin does not imply either grant. |
ROW_CONFLICT / REVISION_CONFLICT | Check existing row content and refresh the table/schema revision before submitting a corrected operation. |
RESOURCE_LIMIT / QUERY_TIMEOUT | Narrow the time range, reduce selected data or batch size, and inspect the active connection budgets. |
NUMERIC_OVERFLOW | An exact sum or numeric result exceeded its type, or a float result became non-finite. Narrow the input or review the stored values. |
COMMIT_NOT_VISIBLE | Check that atLeastCommit came from this table and is visible in the current database instance. |
IDEMPOTENCY_CONFLICT | The request identity was reused with a different body. Retry the original body, or create a new identity for an intentionally different write. |
Keep access and schema under control.
- Key grants
readallows reads and SELECT.writeallows row writes and supported mutations.adminis separate and does not automatically include read or write.- Key lifecycle
- Keys can be issued, rotated, and revoked. A secret is shown once. Save it when issued; a replayed issuance response does not reveal it again.
- Table changes
- Add nullable columns, rename resources, or use logical drop from the console. Review the confirmation carefully; a logical drop is not an immediate physical deletion of stored objects.
- Console connections
- The key stays in page memory. Refreshing or changing the database, organization, or session clears the connection and previous results. Connect again before running a query.
- Metadata
- The metadata panel exposes snapshot and file information with a read key. Use it to inspect the selected database rather than treating it as a live monitoring dashboard.
Understand your database service.
Open a database’s Service tab to review its configured compute routing, storage profile and available controls. Compute capacity and start/stop are operator managed. Runtime status: Not reported means the console has no live process observation.
Backup and recovery are operator managed; this console does not create or restore backups. Ask your administrator about recovery options. Retention controls appear only when the service enables them.
Usage separates sampled storage sizes from activity receipts. Partial observations do not establish zero consumption or a storage allowance. The last activity receipt is not the storage sample time.
What is in the current release?
- Available in the console
- Organizations and team access; databases and typed tables; database keys and HTTPS connections; Insert JSON; SELECT, controlled UPDATE/DELETE; schema changes, metadata inspection and read-only Service configuration.
- API-only, with explicit activation
- Semantic definitions, query jobs, evidence, and usage receipts are available through the database API for enabled contracts. Dedicated console panels are not included yet.
- MQTT · Coming soon
- Database Connections shows the planned MQTT protocol. It is not enabled in this release. Use HTTPS to connect applications to database tables.
- Still being built
- MCP integrations, agent workflows, automation, and agentic analysis dashboards are planned. The current release provides the time-series data foundation.
This is a public pilot. Validate your workload and recovery needs before relying on it for critical data. Usage views and API usage receipts are not invoices.
When something needs a second look.
- 401 or 403
- Check the selected organization, database, key status, and required grant. A database key cannot replace your console session. Reconnect after a refresh or scope change.
- No rows in a result
- Check the table, column names, time filter, and timestamp type. Check that data was inserted into the selected database table.
- A write response was lost
- Retry the original operation with the same request identity and payload. Do not create a new operation for the same write merely because its acknowledgement was lost.
- The database is temporarily unavailable
- The pilot can take time to wake from idle. Retry reads after a short wait. For writes, retain the original operation identity and use its retry action.
- MQTT database connection
- Coming soon. Use HTTPS for this release. Database MQTT setup is not enabled yet.