Metadata-Version: 2.4
Name: time-loc
Version: 1.2.0
Summary: Canonical temporal-spatial primitive for governed systems (time.loc v1.2).
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/governancecommons/time-loc
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: python-ulid
Requires-Dist: base58
Requires-Dist: typing-extensions
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Dynamic: license-file

# time.loc

Canonical temporal-spatial primitive for governed systems. **Apache 2.0.**

time.loc is a 14-field, HMAC-integrity record that answers the fundamental audit question — **when and where did this happen?** — in a single, tamper-evident, version-stamped structure.

Every governed event that needs to be auditable, correlated across systems, or validated retroactively should embed a time.loc record. It is the temporal anchor; nothing is auditable without it.

---

## What it is

- **Dual timestamps** — local (with UTC offset) and UTC. Both are required.
- **Geolocation with provenance** — not just where, but how the location was determined: `declared`, `ip`, `gps`, or `unavailable`. When `unavailable`, all geo fields are null by contract.
- **ULID sequence identifier** — monotonic, globally unique, sortable by string comparison without parsing.
- **HMAC-SHA256 integrity** — over all fields except itself in canonical pipe-delimited form. Unsigned records declare `"unsigned"` explicitly, not null.
- **GPS coordinate fields** (v1.2) — `geo.lat`, `geo.lon`, `geo.alt` for GPS-equipped devices. Null everywhere else.

---

## Spec

See [spec/time-loc-v1.2.md](spec/time-loc-v1.2.md) for the full specification.

Governed by [eco](https://github.com/governancecommons) PASS 0050.02 (accepted 2026-04-04).

---

## Python reference implementation

### Install

```bash
pip install time-loc @ git+https://github.com/governancecommons/time-loc
# or, from a local checkout:
pip install -e /path/to/time-loc
```

Installs three flat top-level modules — `time_loc`, `time_loc_compact`,
`time_loc_validator` — matching the import statements below. Pulls in
`python-ulid` and `base58` automatically.

### Dependencies

```text
pip install python-ulid base58
```

### Produce a record

```python
from time_loc import produce, produce_declared, produce_gps, produce_unsigned_unavailable

# Declared location (human-stated)
record = produce_declared(city="Longview", region="WA", country="US")
print(record.to_dict())

# GPS location
record = produce_gps(latitude=46.138, longitude=-122.938, altitude=12.5)
print(record.to_dict())

# Agent/cloud — no location, no signing key
record = produce_unsigned_unavailable()
print(record.to_dict())
```

### Validate a record

```python
from time_loc_validator import validate_time_loc_v1_2

record_dict = { ... }
errors = validate_time_loc_v1_2(record_dict)
if errors:
    print("invalid:", errors)
else:
    print("valid")
```

### Compact encoding

```python
from time_loc_compact import encode_compact, decode_compact

# Encode a time.loc record dict to a compact base58 string
compact = encode_compact(record.to_dict())

# Decode back to fields 0-9 dict
fields = decode_compact(compact)
```

---

## Files

| File | Description |
| --- | --- |
| `spec/time-loc-v1.2.md` | Full specification |
| `python/time_loc.py` | Producer — frozen dataclass + `produce()` factory |
| `python/time_loc_compact.py` | Compact (base58) encoder/decoder |
| `python/time_loc_validator.py` | Validator — returns list of violation strings |
| `python/time_loc_schema.json` | JSON Schema draft-07 |
| `python/time_loc_examples.json` | Canonical examples (declared, gps, unavailable) |

---

## Used by

[governancecommons/governed-event](https://github.com/governancecommons/governed-event) — the Governed Event Envelope embeds a time.loc record in every governed event.

---

## License

Apache 2.0 — see [LICENSE](LICENSE).

Copyright 2026 Osprey Strategic Holdings LLC.
