Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
> **Alpha — do not use in production.**
> This is pre-MVP software (`0.1.0-alpha.0`). The CLI, the on-disk file format, the SDK surface, and the tag-based identity model are all subject to breaking changes without notice. Use it on throwaway projects or in a sandbox while we stabilize.

Infrastructure-as-code for PostHog. Define dashboards, insights, feature flags, actions, endpoints, event definitions, property groups, experiments, experiment holdouts, and experiment saved metrics in TypeScript, then sync them to a PostHog project with one command.
Infrastructure-as-code for PostHog. Define dashboards, insights, feature flags, actions, endpoints, event definitions, property groups, experiments, experiment holdouts, experiment saved metrics, and logs sampling rules in TypeScript, then sync them to a PostHog project with one command.

## Why

Expand Down Expand Up @@ -51,6 +51,7 @@ export default dashboard({
- `event_definition:read`, `event_definition:write` (also covers property groups)
- `experiment:read`, `experiment:write` (also covers experiment holdouts)
- `experiment_saved_metric:read`, `experiment_saved_metric:write`
- `logs:read`, `logs:write` (logs sampling rules)

Then add it (and your numeric project ID) to a `.env` (or `.envrc`) file:

Expand Down
14 changes: 13 additions & 1 deletion docs/resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,18 @@ Source of truth for the API column: registered viewsets in [`posthog/posthog/api
| Spike detection config | ✅ `environments/{id}/error_tracking/spike_detection_config` | ❌ | |
| Settings | ✅ `environments/{id}/error_tracking/settings` | ❌ | |

## Logs

Rows added ahead of the full matrix refresh (#78). See the parity plan for the
Wave-2 logs verdicts.

| Resource | PostHog API | posthog-definitions | Notes |
| ------------------- | -------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Logs sampling rules | ✅ `projects/{id}/logs/sampling_rules` | ✅ | Identity via `iac:logs-sampling-rules:<key>` marker in `name`. **Order-sensitive** — `priority` (lower first, first-match-wins) is a settable, PATCH-updatable, hashed field. Real PATCH + real DELETE. |
| Logs views | ✅ `projects/{id}/logs/views` | ❌ | Buildable (shipped separately) — `name` marker. |
| Logs metric rules | ✅ `projects/{id}/logs/metric_rules` | ❌ | Gated behind org feature flag `logs-metric-rules`. |
| Logs alerts | ✅ `projects/{id}/logs/alerts` | ❌ | Deferred — `name`-only carrier is notification-facing; destinations are an unreadable imperative subresource. |

## Project & org configuration

| Resource | PostHog API | posthog-definitions | Notes |
Expand All @@ -96,7 +108,7 @@ Source of truth for the API column: registered viewsets in [`posthog/posthog/api

## Summary

Currently shipped: **12 resource types** — Dashboards, Insights, Feature flags, Endpoints, Schema property groups, Event definitions, Experiments, Experiment holdouts, Experiment saved metrics, Project settings, Cohorts, and Actions. Event definitions and property groups together feed `createTypedPostHog`, which wraps any `posthog-js`-shaped client and type-checks `.capture(name, properties)` at compile time against the same specs synced via `apply`. Experiments are declarative across the full lifecycle (draft / running / paused / stopped) — apply drives the launch / pause / resume / end transitions to match. Project settings is the first singleton resource: declared as one block, field-level diff against the live row, declared-only PATCH. Cohorts run before feature flags in the apply order, leaving the door open for cohort-by-key references inside flag conditions.
Currently shipped: **13 resource types** — Dashboards, Insights, Feature flags, Endpoints, Schema property groups, Event definitions, Experiments, Experiment holdouts, Experiment saved metrics, Project settings, Cohorts, Actions, and Logs sampling rules. Event definitions and property groups together feed `createTypedPostHog`, which wraps any `posthog-js`-shaped client and type-checks `.capture(name, properties)` at compile time against the same specs synced via `apply`. Experiments are declarative across the full lifecycle (draft / running / paused / stopped) — apply drives the launch / pause / resume / end transitions to match. Project settings is the first singleton resource: declared as one block, field-level diff against the live row, declared-only PATCH. Cohorts run before feature flags in the apply order, leaving the door open for cohort-by-key references inside flag conditions.

Reasonable IaC targets across the API surface: **~25–30** (cohorts, actions, surveys, annotations, alerts, hog functions/flows, error-tracking rules, warehouse queries, batch exports, …).

Expand Down
25 changes: 25 additions & 0 deletions examples/posthog/logs-sampling-rules/cap_debug_volume.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { logsSamplingRule } from "../../../src/index.js";

// Priority 20 — runs after the path-drop above. Caps debug-level chatter at
// 500 logs/sec (with a small burst) so a runaway loop can't blow the ingestion
// bill, while errors and warnings sail through untouched.
export default logsSamplingRule({
key: "cap_debug_volume",
name: "Rate-limit debug logs",
ruleType: "rate_limit",
priority: 20,
enabled: true,
config: {
logs_per_second: 500,
burst_logs: 2000,
filter_group: {
type: "AND",
values: [
{
type: "AND",
values: [{ key: "severity_text", operator: "exact", value: "debug", type: "log_attribute" }],
},
],
},
},
});
23 changes: 23 additions & 0 deletions examples/posthog/logs-sampling-rules/drop_healthchecks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { logsSamplingRule } from "../../../src/index.js";

// Priority 10 — evaluated first, so the noisiest thing (load-balancer health
// pings) gets dropped before any rate-limit rule wastes budget on it. Order is
// meaningful here: lower priority wins, so keep the drops ahead of the limits.
export default logsSamplingRule({
key: "drop_healthchecks",
name: "Drop /healthz access logs",
ruleType: "path_drop",
priority: 10,
enabled: true,
config: {
filter_group: {
type: "AND",
values: [
{
type: "AND",
values: [{ key: "http.route", operator: "exact", value: "/healthz", type: "log_attribute" }],
},
],
},
},
});
6 changes: 6 additions & 0 deletions openapi-filter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ inverseOperationIds:
- actions_partial_update
- actions_destroy

- logs_sampling_rules_list
- logs_sampling_rules_create
- logs_sampling_rules_retrieve
- logs_sampling_rules_partial_update
- logs_sampling_rules_destroy

unusedComponents:
- schemas
- parameters
Expand Down
18 changes: 18 additions & 0 deletions scripts/smoke-cleanup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ import {
pruneAction,
} from "../src/resources/action/pipeline.js";

import { listManagedLogsSamplingRules } from "../src/resources/logs-sampling-rule/client.js";
import {
logsSamplingRuleKeyFromServer,
pruneLogsSamplingRule,
} from "../src/resources/logs-sampling-rule/pipeline.js";

import type { ClientConfig } from "../src/client/config.js";

type Args = {
Expand All @@ -96,6 +102,7 @@ type Args = {
"property-group"?: string;
cohort?: string;
endpoint?: string;
"logs-sampling-rule"?: string;
};

const ARG_KEYS: Array<keyof Args> = [
Expand All @@ -110,6 +117,7 @@ const ARG_KEYS: Array<keyof Args> = [
"property-group",
"cohort",
"endpoint",
"logs-sampling-rule",
];

function parseArgs(argv: string[]): Args {
Expand Down Expand Up @@ -270,6 +278,16 @@ async function main(): Promise<void> {
(c, row) => pruneEndpoint(c, row),
);
}
if (args["logs-sampling-rule"]) {
await deleteByKey(
"logs-sampling-rule",
args["logs-sampling-rule"],
config,
listManagedLogsSamplingRules,
(row) => logsSamplingRuleKeyFromServer(row),
(c, row) => pruneLogsSamplingRule(c, row),
);
}
}

main().catch((err) => {
Expand Down
21 changes: 19 additions & 2 deletions scripts/smoke.sh
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ SAVED_METRIC_KEY="smoke-metric-${STAMP}"
EXPERIMENT_KEY="smoke-experiment-${STAMP}"
COHORT_KEY="smoke-cohort-${STAMP}"
ENDPOINT_KEY="smoke-endpoint-${STAMP}"
LOGS_SAMPLING_RULE_KEY="smoke-logs-sampling-${STAMP}"

# Names that round-trip cleanly through pull's slug-from-name. We use the
# event-definition `name` as both the spec key AND the on-the-wire event
Expand All @@ -71,7 +72,7 @@ EVENT_NAME="${EVENT_DEFINITION_KEY}"

# Resource kinds we'll seed and pull. Used both in --kind for pull and in
# the no-op assertion's filter (we don't want unrelated kinds dirtying it).
SMOKE_KINDS="insights,dashboards,property-groups,event-definitions,actions,feature-flags,experiment-holdouts,experiment-saved-metrics,experiments,cohorts,endpoints"
SMOKE_KINDS="insights,dashboards,property-groups,event-definitions,actions,feature-flags,experiment-holdouts,experiment-saved-metrics,experiments,cohorts,endpoints,logs-sampling-rules"

cleanup() {
echo
Expand All @@ -88,6 +89,7 @@ cleanup() {
"--property-group=${PROPERTY_GROUP_KEY}" \
"--cohort=${COHORT_KEY}" \
"--endpoint=${ENDPOINT_KEY}" \
"--logs-sampling-rule=${LOGS_SAMPLING_RULE_KEY}" \
|| echo "smoke: cleanup hit an error (continuing)"
echo "smoke: removing workdir $WORK_DIR"
rm -rf "$WORK_DIR"
Expand Down Expand Up @@ -153,7 +155,8 @@ mkdir -p \
"$SEED_DIR/experiment-saved-metrics" \
"$SEED_DIR/experiments" \
"$SEED_DIR/cohorts" \
"$SEED_DIR/endpoints"
"$SEED_DIR/endpoints" \
"$SEED_DIR/logs-sampling-rules"

# Insight — referenced by the dashboard tile below.
cat > "$SEED_DIR/insights/smoke-insight.ts" <<EOF
Expand Down Expand Up @@ -309,6 +312,20 @@ export default endpoint({
});
EOF

# Logs sampling rule — standalone; identity marker rides in \`name\`. Seeded
# disabled so it never touches ingestion.
cat > "$SEED_DIR/logs-sampling-rules/smoke-logs-sampling.ts" <<EOF
import { logsSamplingRule } from "@posthog/definitions";
export default logsSamplingRule({
key: "${LOGS_SAMPLING_RULE_KEY}",
name: "${LOGS_SAMPLING_RULE_KEY}",
ruleType: "rate_limit",
priority: 100,
enabled: false,
config: { logs_per_second: 1000000 },
});
EOF

# Apply the seed (no --prune so unrelated iac-tagged rows stay intact).
$CLI apply --dir "$SEED_DIR" --json | jq '{totals, byResource}'

Expand Down
Loading