Output

Time API

A simple REST API for current time information with timezone support.

Repository: https://github.com/csmith416/smith-time

Served at / in the container, typically /time-api behind Caddy.

Authorize (Session-only)

Use the banner at the top of the page to set either your X-API-Key (user) or X-Admin-Token (admin). Refresh clears it.

Overview

This service provides a small, stable HTTP API for retrieving current time data with timezone support.

Production Base URL
https://smith-lab.ddns.net/time-api
Local Base URL
http://localhost:8080
Quickstart
1) Create an API key (Admin) → 2) Call /v1/time with X-API-Key.
Windows PowerShell note: use curl.exe (PowerShell 5.1 aliases curl to Invoke-WebRequest).
# time
curl -H "X-API-Key: <api-key>" "https://smith-lab.ddns.net/time-api/v1/time?timezone=UTC"

Authentication

API Key (User Requests)

User endpoints (/v1/time, /v1/timezones) may require an API key via the header below.

Header
X-API-Key: <api-key>

Enforcement is controlled by REQUIRE_API_KEY:

  • unset (default): auto — requires a key only after at least one key exists.
  • true: required — always requires a key (returns 503 if no keys exist).
  • false: disabled — key is not required.

Admin Token (Key Management)

Admin endpoints (/v1/keys, revoke endpoints) require the admin token header below.

Header
X-Admin-Token: <admin-token>

Configure one of:

  • ADMIN_TOKEN_SHA256_HEX (preferred) — hex-encoded SHA-256 of the token
  • ADMIN_TOKEN (fallback) — plaintext token (will be hashed in-memory)

If admin token is not configured, admin endpoints return 503 with {"error":"admin token not configured"}.

Configuration

Configuration is done via environment variables.

Variable Description Default
KEY_DB_PATH Where API keys are stored (hashed JSON file). /data/keys.json if /data exists; otherwise data/keys.json
REQUIRE_API_KEY Controls API key enforcement mode (auto/required/disabled). unset (auto)
ADMIN_TOKEN_SHA256_HEX Preferred admin token configuration (hex SHA-256 of token). unset
ADMIN_TOKEN Fallback admin token configuration (plaintext; hashed in-memory). unset
STATIC_DIR Dev-only: serve UI assets from disk instead of embedded files. unset (use embedded)
Recommended production settings
# Require API keys once you have them
REQUIRE_API_KEY=true

# Store keys on the persistent /data volume
KEY_DB_PATH=/data/keys.json

# Admin token stored as a SHA-256 hex hash (no plaintext in env)
ADMIN_TOKEN_SHA256_HEX=<hex_sha256_of_token>

CLI

The built binary includes a small key-management CLI. It operates on the same key DB path as the server.

Create key (one-time view):

./time-api keys create --name "my-service"

List keys (no plaintext):

./time-api keys list

Revoke key:

./time-api keys revoke <id>

Errors

All JSON errors use the same shape:

{"error": "..."}
Status When it happens Example
400 Invalid request (example: bad timezone) {"error":"Invalid timezone"}
401 Missing/invalid X-API-Key or X-Admin-Token {"error":"unauthorized"}
404 Unknown endpoint or missing key id {"error":"Page not found"}
405 Unsupported method on an endpoint {"error":"method not allowed"}
500 Server error (storage/read/verify failures) {"error":"internal error"}
503 Auth configured but prerequisites missing (no keys / admin token not configured) {"error":"no api keys configured"}

Operations

CORS
All endpoints respond with CORS headers and handle OPTIONS preflight.
Request IDs
Every response includes X-Request-ID for log correlation.
Key Storage
Keys are hashed at rest; plaintext is returned only once at creation.
Admin UI Token
Authorization is session-only (refresh clears it).

API Endpoints

Method Path Auth Description
GET /v1/time API key (conditional) Current time in a requested timezone
GET /v1/timezones API key (conditional) All supported timezones
GET /v1/healthz None Liveness/health check
GET /v1/version None Build/version info
GET /v1/status None Operational status (non-sensitive)
GET /v1/openapi.json None OpenAPI 3.0.3 spec
GET POST /v1/keys Admin token List keys / create key (one-time view)
POST /v1/keys/{id}/revoke Admin token Revoke a key
DELETE /v1/keys/{id} Admin token Revoke a key (alternate route)

Get Current Time

GET /v1/time?timezone={timezone}

Headers: X-API-Key (required if key enforcement is enabled)

Query parameters:

  • timezone (optional): e.g. UTC, Europe/London. Defaults to UTC.

Example call:

Try it
curl -H "X-API-Key: <api-key>" "https://smith-lab.ddns.net/time-api/v1/time?timezone=UTC"

Status codes:

  • 200 OK
  • 400 Invalid timezone
  • 401 Unauthorized (missing/invalid API key)
  • 503 No API keys configured (when enforcement is required)

Sample response:

{
  "timestamp": 1634567890,
  "time": "10:13:44.323435",
  "date": "2021-10-18",
  "timezone": "UTC",
  "abbr": "GMT",
  "offset": "+00:00",
  "iso8601": "2021-10-18T10:13:44+00:00"
}

Sample error (invalid timezone):

{"error":"Invalid timezone"}

List Available Timezones

GET /v1/timezones

Headers: X-API-Key (required if key enforcement is enabled)

Query parameters: none

Example call:

Try it
curl -H "X-API-Key: <api-key>" "https://smith-lab.ddns.net/time-api/v1/timezones"

Status codes:

  • 200 OK
  • 401 Unauthorized (missing/invalid API key)
  • 503 No API keys configured (when enforcement is required)

Sample response:

{
  "timezones": [
    {"name": "Africa/Abidjan", "abbr": "GMT"},
    {"name": "Africa/Accra", "abbr": "GMT"},
    {"name": "Europe/London", "abbr": "BST"}
  ]
}

Health

GET /v1/healthz

Headers: none

Query parameters: none

Example call:

Try it
curl "https://smith-lab.ddns.net/time-api/v1/healthz"

Status codes: 200 OK

Sample response:

{
  "ok": true,
  "status": "ok",
  "time_utc": "2026-02-01T12:34:56.123456789Z",
  "uptime_seconds": 12345
}

Version

GET /v1/version

Headers: none

Query parameters: none

Example call:

Try it
curl "https://smith-lab.ddns.net/time-api/v1/version"

Status codes: 200 OK

Sample response:

{
  "version": "dev",
  "commit": "",
  "build_date": ""
}

Status

GET /v1/status

Headers: none

Query parameters: none

Example call:

Try it
curl "https://smith-lab.ddns.net/time-api/v1/status"

Status codes: 200 OK, 500 internal error

Sample response:

{
  "ok": true,
  "time_utc": "2026-02-01T12:34:56.123456789Z",
  "uptime_seconds": 12345,
  "api_key_mode": "auto",
  "keys_total": 3
}

OpenAPI

GET /v1/openapi.json

Headers: none

Query parameters: none

Example call:

Try it
curl "https://smith-lab.ddns.net/time-api/v1/openapi.json"

Status codes: 200 OK

Returns the OpenAPI spec for the service.

Tip: import this into Postman/Insomnia or use it to generate a client.

Admin Keys

GET /v1/keys
POST /v1/keys

Headers: X-Admin-Token (required)

Query parameters (GET):

  • include_revoked (optional): set to true to include revoked keys (default: omitted/false)

What each method does:

  • GET /v1/keys: returns active keys only by default (no plaintext; includes id, name, timestamps, last4).
  • POST /v1/keys: creates a new key and returns api_key once.

Body (POST):

  • name (required): friendly label for the key (recommended: service or human owner)

No plaintext keys are stored on disk. api_key is returned only on create.

Example call (GET):

Try GET
curl -H "X-Admin-Token: <admin-token>" "https://smith-lab.ddns.net/time-api/v1/keys"
Try GET

Example call (POST):

Try POST

Bash/macOS/Linux:

curl -H "X-Admin-Token: <admin-token>" \
        -H "Content-Type: application/json" \
        -d '{"name":"my-service"}' \
        "https://smith-lab.ddns.net/time-api/v1/keys"

Windows PowerShell 5.1 (use curl.exe):

curl.exe -H "X-Admin-Token: <admin-token>" `
        -H "Content-Type: application/json" `
        -d '{"name":"my-service"}' `
        "https://smith-lab.ddns.net/time-api/v1/keys"
Try POST

Status codes:

  • 200 OK (GET)
  • 201 Created (POST; plaintext key shown once)
  • 401 Unauthorized (missing/invalid admin token)
  • 503 Admin token not configured
  • 405 Method not allowed

Sample response (GET):

[
  {"id": "9978eac73fdcc28a", "name": "web-test", "created_at": "2026-02-01T20:51:23Z", "last4": "eTgA"}
]

Sample request (POST):

{"name": "my-service"}

Sample response (POST, shown once):

{"api_key": "slk_...", "id": "...", "name": "my-service", "created_at": "2026-02-01T20:51:23Z", "last4": "eTgA"}

Revoke Key

POST /v1/keys/{id}/revoke

Headers: X-Admin-Token (required)

Path parameters:

Example call:

Try it
curl -X POST -H "X-Admin-Token: <admin-token>" "https://smith-lab.ddns.net/time-api/v1/keys/<id>/revoke"

Status codes:

Sample response:

{"ok": true}

Revoke Key (Alternate)

DELETE /v1/keys/{id}

Headers: X-Admin-Token (required)

Path parameters:

Example call:

Try it
curl -X DELETE -H "X-Admin-Token: <admin-token>" "https://smith-lab.ddns.net/time-api/v1/keys/<id>"

Status codes:

Sample response:

{"ok": true}

Some endpoints require X-API-Key (for example /v1/time and /v1/timezones).

robots.txt

GET /robots.txt

Always allows crawling.

Example call:

curl "https://smith-lab.ddns.net/time-api/robots.txt"

CORS

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-API-Key, X-Admin-Token