ifdd-ts-core
Shared runtime of the IFDD time series API. Instances are generated from the
template ifdd-de/ts-api; this package
holds everything they have in common: token and store checks, the check of
samples against the vocabularies of the type service, reading and writing in
VictoriaMetrics, and the documentation pages.
service (client credentials) --HTTPS--> proxy --> ts-api (ifdd-ts-core) --> VictoriaMetrics
| ^
+--> type service |
Grafana (ifdd-ts) reads all ----+
- Distribution
ifdd-ts-core, importifdd.ts.core(ifddandifdd.tsare namespace packages). Python 3.11 or later. - License BSD-3-Clause. Swagger UI and ReDoc are included as third-party files (see below).
Use
from ifdd.ts.core import create_app
from app.settings import settings # a subclass of ifdd.ts.core.CoreSettings
app = create_app(settings)
or uvicorn --factory ifdd.ts.core:create_app with the settings in the
environment. Every endpoint is below the path prefix IFDD_TS_API_PATH
(default /tsapi), so the API can share a host with Grafana behind the
proxy without a rewrite rule.
Configuration
Environment variables (or .env):
| Variable | Default | Meaning |
|---|---|---|
IFDD_TS_API_PATH |
/tsapi |
path prefix of every endpoint; empty serves at the root |
IFDD_TS_API_VERSION |
package version | version shown by /version and the OpenAPI document |
IFDD_TS_API_AUTH_ENABLED |
true |
false: no token is checked, every caller is a development user with both roles and the pattern IFDD_CONTEXT_ROOT |
IFDD_OIDC_ISSUER |
– | expected iss; empty: not checked (warning) |
IFDD_OIDC_JWKS_URL |
– | keys of the realm |
IFDD_OIDC_TOKEN_URL |
– | only shown in the API description |
IFDD_OIDC_JWKS_AUDIENCE |
ifdd-ts-api |
required aud; the client that carries the roles |
IFDD_TS_ROLE_READ, IFDD_TS_ROLE_WRITE |
ifdd_ts_ro, ifdd_ts_rw |
roles to read and to write |
IFDD_CONTEXT_ROOT |
ifdd.de |
store pattern of the development user |
IFDD_TS_VM_URL |
http://victoriametrics:8428 |
VictoriaMetrics |
IFDD_TYPE_URL |
http://type/v1 |
type service; every property must be below it |
IFDD_TS_MAX_ITEMS |
10000 |
most samples written or read, and most series listed, per request |
IFDD_TS_DEFAULT_RANGE_DAYS |
30 |
time range of /stores and /series without start |
IFDD_TS_LOOKBACK_DAYS |
3660 |
how far back the latest sample is searched without max_age |
IFDD_TS_VOCABULARY_CACHE_TTL |
300 |
seconds a vocabulary is kept |
IFDD_TOKEN_CACHE_SIZE, IFDD_TOKEN_CACHE_TTL |
1000, 300 |
cache of validated tokens |
IFDD_TS_VM_TIMEOUT, IFDD_TS_VOCABULARY_TIMEOUT |
30, 10 |
seconds |
The limit of 10 000 is a starting value, not a measured one.
TLS: calls to the type service (and to the JWKS URL) check certificates
against the system trust store, not against certifi; put the CA of the
proxy there, as the ts-api image does.
Endpoints
All below IFDD_TS_API_PATH:
| Method and path | Role | Purpose |
|---|---|---|
GET /version |
none | version (health checks) |
GET /whoami |
valid token | roles and store patterns read from the token |
GET /stores |
read | stores with samples that the caller may read |
GET /stores/{store}/series |
read + pattern | series of a store, filtered by labels |
GET /stores/{store}/samples |
read + pattern | latest sample of one series at or before at |
GET /stores/{store}/samples/range |
read + pattern | raw samples of the series of an object in [start, end] |
POST /stores/{store}/samples |
write + pattern | write one sample |
POST /stores/{store}/samples/batch |
write + pattern | write a list of samples (all or nothing on checks) |
GET /docs, GET /redoc, GET /openapi.json |
none | documentation |
Order of checks: token (401), store name (422), role (403), store pattern (403), body (422), vocabulary (422, or 502 if the type service fails), VictoriaMetrics (502).
Stores and access
A store is a named part of the one VictoriaMetrics instance: every sample
carries the label ifdd_store. The API passes extra_label=ifdd_store=<store>
on every call that reads or writes samples. VictoriaMetrics then sets the
label on every imported sample, overriding one in the body, and adds it as a
filter to every read (checked against v1.153.0). That parameter alone keeps
stores apart; the tests fail without it.
Store names are written like DNS names: at least two labels of lower-case
letters, digits and inner hyphens, e.g. temperature.ops.example.ifdd.de.
Any such name is a store; it exists once a sample was written to it.
Callers are services with an access token of the client credentials grant.
The token's audience must be IFDD_OIDC_JWKS_AUDIENCE, and its client roles
there decide:
ifdd_ts_roreads,ifdd_ts_rwwrites; they apply to every store the caller's patterns open;- a role with a dot is a store pattern. It opens the store of that name
and every store below it, compared at label boundaries
(
ops.example.ifdd.deopenstemperature.ops.example.ifdd.de, notxops.example.ifdd.de). The rootifdd.deopens all.
A foreign store answers 403, not 404. /stores leaves out stores the caller
may not read.
What this does not protect. Grafana (ifdd-ts) reads VictoriaMetrics directly and shows the series of every store; containers on the ingest network write to VictoriaMetrics directly, without any check. Both are deliberate (see the documentation of ifdd-ts); the API is the way in for services that should not get either.
Series and samples
A series is named by four labels, the series contract of ifdd-ts:
| Field | Label | Value |
|---|---|---|
name |
__name__ |
metric name, only for people: letters, digits, _ |
ifdd_id |
ifdd_id |
full IRI of the object in ifdd-be |
ifdd_property |
ifdd_property |
full IRI of the property in the type service |
ifdd_unit |
ifdd_unit |
full IRI of the unit |
No further labels are accepted. A value is a finite number; states are coded as numbers.
Times are ISO 8601 with an offset (Z, +02:00), at most to the
millisecond, not before 1970. Without offset or with finer precision the API
answers 422 instead of guessing or cutting. Answers are UTC with three
decimals (2026-10-22T07:15:00.250+00:00).
Appending only. There is no PUT: VictoriaMetrics cannot replace a single
sample. Two samples of the same series and time are both kept, unless
deduplication is on (-dedup.minScrapeInterval=1ms, as in ifdd-ts); then
the larger value stays, not the later one (checked against v1.153.0).
Sending the same sample twice is therefore harmless.
Latest sample. GET /stores/{store}/samples needs ifdd_id and
ifdd_property; if more than one series has them (other name or unit), it
answers 422 and asks for name and ifdd_unit. It searches back from at
in growing windows (one hour, one day, 30 days, 366 days, then
IFDD_TS_LOOKBACK_DAYS or max_age), so a recent sample costs one small
read.
Checks against the vocabulary
Before writing, every sample is checked against the vocabulary of its property in the type service:
ifdd_propertymust be belowIFDD_TYPE_URL. Its namespace is the IRI up to the last/or#; the type service answers the namespace IRI with the vocabulary.- The vocabulary must define the property as an
rdf:Property. - If the property lists
qudt:applicableUnit,ifdd_unitmust be one of them. - If it has
sh:minInclusiveorsh:maxInclusive, the value must lie within.
The vocabulary is read with a JSON-LD processor, so prefixes and term names
do not matter; its context must be inline. A vocabulary is kept for
IFDD_TS_VOCABULARY_CACHE_TTL seconds; failures are not kept. A batch is
checked completely before anything is written; the 422 answer names every
bad sample by its index.
Relation to ifdd-be-core
The token check and the documentation pages are copies of the ones in
ifdd-be-core, by decision (a shared package would have made this one
depend on PostgreSQL, rdflib and the backend settings). Fixes in one belong
in the other. If a third service needs them, they move into a package of
their own. One difference on purpose: a cached token is checked for expiry
on every use.
Development
pip install -e '.[dev]'
pytest
tests/test_vm_integration.py runs against a real VictoriaMetrics when
IFDD_TEST_VM_URL is set; start it with deduplication like ifdd-ts:
victoria-metrics-prod -storageDataPath=/tmp/vm -httpListenAddr=127.0.0.1:8428 \
-dedup.minScrapeInterval=1ms -retentionPeriod=10y &
IFDD_TEST_VM_URL=http://127.0.0.1:8428 pytest
The CI does the same with the release binary v1.153.0 (SHA-256 checked).
Documentation: docs/en and docs/de (Sphinx, MyST);
python tests/check_docs.py . --prefix ifdd-ts-core- after exporting the
OpenAPI document to docs/<lang>/openapi.json (see api-http.md).
Third-party files
src/ifdd/ts/core/docs_assets/ holds unchanged copies of these npm packages,
the same as in ifdd-be-core; docs_assets/assets.json names version,
tarball, npm integrity and the SHA-256 of every file, and
tests/test_docs_assets.py checks them.
| Package | Version | License |
|---|---|---|
swagger-ui-dist |
5.33.0 | Apache-2.0 (with NOTICE) |
redoc |
2.5.4 | MIT |
The license texts are shipped next to the files and in the wheel; the bundles
contain code under further licenses (see the *.LICENSE.txt files).
docs_assets/ifdd/ is part of this package (BSD-3-Clause). Update them
together with ifdd-be-core, as described in its README.
AI assistance
Parts of this repository, especially tests, test data and documentation, were developed or improved with the help of large language models. The maintainer reviewed and approved every change.
License
BSD-3-Clause, see LICENSE.
Metadata
Release files for ifdd-ts-core 26.10.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| ifdd_ts_core-26.10.2.tar.gz | 834.0 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ifdd_ts_core-26.10.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.7 MB
Release files / ifdd_ts_core-26.10.2.tar.gz
| Download URL | ifdd_ts_core-26.10.2.tar.gz |
|---|---|
| Size | 834.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
72d25b64f34381cfdfdd609cae89c1a8b241ec37f8c3be63eba34fb94c474f7a
|
|
BLAKE2b-256 checksum How to use checksums |
d7d696e2e86ccc587403b2efb93b1908f8d3faaad1a1672e2b9d390099b8a372
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logRelease files / ifdd_ts_core-26.10.2-py3-none-any.whl
| Download URL | ifdd_ts_core-26.10.2-py3-none-any.whl |
|---|---|
| Size | 832.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
27c5a8fad31332f1e7e993bb9d863cf197281c9a9a959502c0d61737011559ed
|
|
BLAKE2b-256 checksum How to use checksums |
51217ea701ffa6d2041d74c55f8175e5ba275b1bd15670793d6bbcdfe287e73d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logSecurity for ifdd-ts-core 26.10.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for ifdd_ts_core-26.10.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.