diff --git a/docs/api/public-api.mdx b/docs/api/public-api.mdx index 35a0cd3d..90461020 100644 --- a/docs/api/public-api.mdx +++ b/docs/api/public-api.mdx @@ -141,7 +141,7 @@ Configuration and revision responses never include secret names, values, or ciph `POST /configuration/plan` accepts the same complete desired configuration as `PUT /configuration`. It performs no writes and returns the authoritative `target`, `action`, `currentVersion`, `desiredVersion`, and structured `changes` (`field`, `from`, and `to`). Service IDs on these flat service routes are authorized installation-globally rather than being scoped by a project ID in the URL. -`PUT /configuration` is atomic and replaces the complete managed configuration. Send the `currentVersion` returned by the plan as one quoted strong ETag (for example, `If-Match: "sha256:…"`). Missing, unquoted, multiple, weak, or otherwise invalid headers are rejected; a service change after planning returns `409 CONFIGURATION_PLAN_STALE`. A successful response returns the authoritative target and the structured change set that was applied. The request must contain exactly `name`, `source`, `hostname`, `ports`, `placement`, `healthCheck`, `startCommand`, and `resources`. Omitted or unknown fields are rejected. `hostname` must be concrete and non-null. Use `null` to clear nullable fields, including `resources`. +`PUT /configuration` is atomic and replaces the complete managed configuration. Send the `currentVersion` returned by the plan as one quoted strong ETag (for example, `If-Match: "sha256:…"`). Missing, unquoted, multiple, weak, or otherwise invalid headers are rejected; a service change after planning returns `409 CONFIGURATION_PLAN_STALE`. A successful response returns the authoritative target and the structured change set that was applied. The request must contain exactly `name`, `source`, `hostname`, `ports`, `placement`, `healthCheck`, `startCommand`, `resources`, and `crons`. Omitted or unknown fields are rejected. `hostname` must be concrete and non-null. Use `null` to clear nullable fields, including `resources`. Replacing the final public HTTP port with a domain automatically disables serverless in the mutable service configuration. The plan reports this side effect as a `serverless.enabled` change before apply. You do not send serverless settings in the replacement request. @@ -177,11 +177,19 @@ Replacing the final public HTTP port with a domain automatically disables server "resources": { "cpuCores": 1, "memoryMb": 512 - } + }, + "crons": [ + { + "path": "/api/cron/digest", + "schedule": "0 5 * * *" + } + ] } ``` -The `tc apply` command sends this complete replacement. A linked `techulus.yml` stores only `target.serviceId`; desired service configuration remains under `service`. Explicit CLI targeting uses `--service `. During interactive `tc link`, a project with exactly one environment selects it automatically; zero environments is an error and multiple environments prompt for a choice. +`crons` is the complete desired list of UTC service cron definitions. Each item contains exactly `path` and `schedule`. Paths must be unique, origin-relative paths without a query string or fragment. Send an empty list to remove all cron definitions. `current.crons` returns definitions only; runtime status remains outside the configuration fingerprint. Cron-only changes take effect without creating a deployment revision. See [Service crons](/services/configuration#service-crons) for request and secret behavior. + +The `tc apply` command sends this complete replacement. A linked `techulus.yml` stores its target identity under `target.serviceId` and desired configuration under `service`. `tc link` writes current cron definitions into the manifest, and `tc apply` treats that manifest list as authoritative. Explicit CLI targeting uses `--service `. During interactive `tc link`, a project with exactly one environment selects it automatically; zero environments is an error and multiple environments prompt for a choice. The API supports these source variants: @@ -341,4 +349,4 @@ The CLI uses the same endpoints documented above: | `tc metrics` | Query service metrics | | `tc revisions` | List the redacted revision changelog | -`tc link` stores only the selected `target.serviceId` in `techulus.yml`. Relinking a manifest to a different service requires removing `target.serviceId` first. `tc apply` always displays the server-generated plan and prompts before writing; use `tc apply --yes` for noninteractive automation. Image and GitHub services use the same `tc link`, `tc apply`, `tc deploy`, and inspection commands. +`tc link` stores the selected `target.serviceId` and current managed configuration, including cron definitions, in `techulus.yml`. Relinking a manifest to a different service requires removing `target.serviceId` first. `tc apply` always displays the server-generated plan and prompts before writing; use `tc apply --yes` for noninteractive automation. Image and GitHub services use the same `tc link`, `tc apply`, `tc deploy`, and inspection commands. diff --git a/docs/infrastructure/logging.mdx b/docs/infrastructure/logging.mdx index 0f060c3c..abb36618 100644 --- a/docs/infrastructure/logging.mdx +++ b/docs/infrastructure/logging.mdx @@ -3,7 +3,7 @@ title: "Logging" description: "Centralized log aggregation with Victoria Logs." --- -Techulus Cloud uses [Victoria Logs](https://docs.victoriametrics.com/victorialogs/) for centralized log aggregation. All log types — container output, HTTP access logs, build logs, and agent system logs — are collected and searchable from the web UI. +Techulus Cloud uses [Victoria Logs](https://docs.victoriametrics.com/victorialogs/) for centralized log aggregation. Container output, HTTP access logs, service cron history, build logs, and agent system logs are collected and searchable from the web UI. ## Log Types @@ -11,6 +11,7 @@ Techulus Cloud uses [Victoria Logs](https://docs.victoriametrics.com/victorialog | --- | --- | --- | | `container` | Container stdout/stderr | `deployment_id`, `service_id`, `server_id`, `stream` | | `http` | Traefik access logs (proxy nodes) | `service_id`, `host`, `method`, `path`, `status`, `duration_ms`, `client_ip` | +| `cron` | Scheduled service HTTP requests | `service_id`, `cron_id`, `path`, `result`, `status`, `duration_ms` | | `build` | BuildKit image builds | `build_id`, `service_id`, `project_id` | | `agent` | Agent process output | `server_id`, `level` | @@ -20,10 +21,11 @@ Each agent ships logs directly to Victoria Logs over HTTP using the JSON Lines f - **Container logs** are streamed from running containers, batched in groups of 1000, and flushed every 5 seconds. Log positions are tracked per container to prevent duplicates. - **HTTP logs** are tailed from Traefik's access log file on proxy nodes, batched in groups of 500. +- **Cron logs** are written by the control plane after each scheduled request with its result, HTTP status, and duration. They never include `CRON_SECRET`, the base URL, or response bodies. - **Build logs** are captured during image builds and streamed in real time. - **Agent logs** intercept the agent's own stdout/stderr with automatic log level detection. -All log batches retry up to 3 times with exponential backoff on failure. +Agent-originated log batches retry up to 3 times with exponential backoff on failure. Cron history writes are best-effort and do not retry the cron request when log ingestion fails. ## Configuration @@ -42,3 +44,5 @@ The control plane exposes logs at `https://logs.` with basic auth. Logs are accessible from the web UI for each service, deployment, build, and server. The control plane queries Victoria Logs using LogSQL with filters for `service_id`, `deployment_id`, `server_id`, and time ranges. Searches run against Victoria Logs rather than only the entries currently loaded in the browser. Continuous service, request, and server log views default to the last 24 hours and support 1-hour, 6-hour, 24-hour, and 7-day ranges. These query ranges do not change the separate `VL_RETENTION` storage setting. + +Cron history follows `VL_RETENTION`. The latest cron result remains visible in the read-only **Crons** section of [service configuration](/services/configuration#service-crons). diff --git a/docs/services/configuration.mdx b/docs/services/configuration.mdx index d16fed89..f01be417 100644 --- a/docs/services/configuration.mdx +++ b/docs/services/configuration.mdx @@ -22,6 +22,34 @@ Each secret has a key and an encrypted value, scoped to a single service. You ca Secrets are passed as environment variables to the container when it starts. +## Service crons + +Service crons send scheduled HTTP `GET` requests to your application. Define them under `service.crons` in `techulus.yml`: + +```yaml +service: + crons: + - path: /api/cron/digest + schedule: "0 5 * * *" + - path: /api/cron/cleanup + schedule: "0 * * * *" +``` + +Each path must be unique, start with `/`, and omit a query string or fragment. Schedules use five-field cron expressions in UTC. You can configure up to 100 crons per service. + +Add these service secrets from the web UI: + +| Secret | Required | Description | +| --- | --- | --- | +| `CRON_BASE_URL` | Yes | Public HTTP or HTTPS origin joined with each configured path | +| `CRON_SECRET` | No | Sent as `Authorization: Bearer `; requires an HTTPS base URL | + +The control plane only connects to public destinations. It blocks private and reserved addresses, does not follow redirects, and gives each request a 10-second deadline. Cron requests do not retry. If the control plane misses multiple intervals, it sends only the latest due occurrence instead of backfilling every missed run. + +Missing or invalid configuration produces a skipped run. Redirects and non-2xx responses produce failed runs. The read-only **Crons** section in service configuration shows the latest result. Full history is available in [service logs](/infrastructure/logging) and follows the configured log retention period. + +`tc apply` treats `service.crons` as the complete desired list. Removing the field or setting it to an empty list removes all cron definitions. Cron-only changes take effect without creating a deployment revision. + ## Start Command Override the container's default entrypoint by setting a custom start command. This is useful when deploying from pre-built images that need different startup behavior.