Skip to content
GitHubGet started

Contributing

Contributing

Repository layout of the server, the Flutter SDK and these docs, how to run the tests, the migration and data-layer rules, how to propose changes, and the three licenses.

DecTog is four repositories under github.com/DecTog:

Repository Contents License
DecTog/dectog The server: Cloudflare Worker, D1 database, Next.js dashboard and marketing site AGPL-3.0
DecTog/flutter_dectog The Flutter SDK, the flutter_dectog package (not yet published on pub.dev) MIT
DecTog/react-native-dectog The React Native and Expo SDK, the react-native-dectog package (not yet published on npm) MIT
DecTog/docs This site Content CC BY 4.0, site code MIT
Path What it is
apps/worker/src/ The Worker: index.ts routes hosts and paths; redirect.ts is the link engine; api_links.ts, api_clicks.ts, api_installs.ts, api_events.ts are the SDK API; attribution.ts, fraud.ts, asa.ts decide verdicts; api_admin.ts, api_auth.ts, api_google.ts, api_customers.ts, api_attribution_admin.ts, api_blog.ts serve the dashboard; auth.ts, ratelimit.ts, webhook.ts, wellknown.ts, ogpreview.ts, collect.ts, seed.ts, bootstrap.ts
apps/worker/src/db/ The typed data layer: tables.ts (column registry), write.ts (insertStmt, updateById), links.ts, tempLinks.ts, users.ts (the shared lookups)
apps/worker/migrations/ 0001_schema.sql (the whole schema), then one numbered file per later change
apps/worker/test/ Vitest suites on the Workers pool with an isolated D1
apps/worker/wrangler.jsonc Dev defaults and the production environment
wrangler.jsonc, package.json (root) The Deploy to Cloudflare button’s deployment on workers.dev and its build and deploy commands. See One click on workers.dev
apps/web/src/app/ The Next.js app: (site) marketing pages, login, reset-password, dashboard/**
apps/web/public/SKILL.md, llms.txt The agent guide and index, served at https://dectog.com/SKILL.md and /llms.txt
docs/API_CONTRACT.md The SDK-to-worker contract, route by route
deploy/terraform/ A Terraform module for the Cloudflare resources of a self-hosted deployment: the zone, DNS records, the D1 database, the optional exports bucket and custom-domain fallback origin. See Deploy to Cloudflare
Path What it is
lib/flutter_dectog.dart The public entry point
lib/src/dectog_impl.dart FlutterDectog: initialization, link handling, install registration, claims, events, link CRUD
lib/src/models/ DectogConfig, DectogLink, DectogTempLink, DectogLinkResolution, DectogLinkClick, DectogAppInstallRequest, DectogAppInstallResponse, DectogClickParams, DectogError
lib/src/services/ Device identity and hardware, the install referrer service, the install state store (the .dectog_install.json sentinel), the event queue, request signing, the retry policy, the sensitive-URL filter, the link deduper
lib/src/widgets/dectog_paste_button.dart DectogPasteButton
android/src/main/kotlin/app/dectog/core/ The framework-free Android reads every DecTog SDK shares as source: DectogDevice.kt (device id, carrier, package and launch info, Meta referrer, App Links state), InstallReferrers.kt (Play, Huawei, Samsung, Xiaomi and vivo readers), Pasteboard.kt
android/src/main/kotlin/app/dectog/flutter_dectog/ DectogIdentifiersPlugin.kt, which maps the method channel onto app.dectog.core
ios/flutter_dectog/ The Swift package. Framework-free, shared as source: DectogDevice.swift, DectogInstallDates.swift, DectogKeychain.swift, DectogPasteTargetView.swift; Flutter glue: DectogIdentifiersPlugin.swift, DectogPasteControl.swift. ios/flutter_dectog.podspec for CocoaPods hosts
example/ Trails, the example app: shared links, invites and referrals, onboarding, identify, a demo purchase, screen views. Reads env/dev.json for DECTOG_API_KEY, DECTOG_BASE_URL, DECTOG_LINK_ROOT_DOMAIN; example/site/ is its store-badges page
test/ Unit tests for the models, the referrer service, the state store, the event queue, signing, the retry policy, the URL parser and the paste button
doc/api-reference.md, llms.txt The API reference this site syncs, and the agent index generated from it (dart run tool/gen_llms.dart)

A port of flutter_dectog on the same contract and fixtures; Porting the SDK explains what it shares and what it reimplements.

Path What it is
src/Dectog.ts DectogSdk and the Dectog singleton: initialization, link handling, install registration, claims, events, link CRUD (the port of lib/src/dectog_impl.dart)
src/internal/ The services: install state, event queue, request signing with its own SHA-256 and HMAC, the retry policy, the URL parser, the link filter and deduper, the referrer and pasteboard gates
src/models/, src/config.ts, src/errors.ts The public types, DectogConfig and DectogError
src/navigation.ts, src/PasteButton.tsx trackNavigation for React Navigation and Expo Router, and DectogPasteButton
ios/Shared/, android/src/main/java/app/dectog/core/ Copies of the shared native reads; tool/sync_shared.sh --check fails on any difference from flutter_dectog
ios/DectogModule.swift, android/.../reactnative/DectogModule.kt The Expo module: the bridge onto the shared reads
plugin/src/ The config plugin: associated domains, URL types, intent filters; app.plugin.js loads it
test/ Jest suites against a fake native layer and a recording network; test/fixtures/ are the shared wire fixtures and URL cases
example/ An Expo app with the plugin configured; expo prebuild generates its native projects
doc/api-reference.md, llms.txt The API reference this site syncs, and the agent index (npm run llms)
Path What it is
src/content/docs/<group>/<slug>.md One page per file; the sidebar groups are fixed in astro.config.mjs and pages are ordered by sidebar.order
src/components/Footer.astro The footer with the version note (the SDK versions and the latest migration) and the license split
wrangler.jsonc The Worker that serves dist/ on the route docs.dectog.com/*
.github/workflows/deploy.yml Builds on every push and pull request; deploys on push to main
# Server
cd apps/worker && npm ci && npm test # Vitest on the Workers pool (on macOS: npm run test:split)
cd apps/worker && npm run typecheck # tsc --noEmit
cd apps/web && npm ci && npm run typecheck
# SDKs
cd flutter_dectog && flutter pub get && flutter analyze && flutter test
cd react-native-dectog && npm ci && npm test && npm run typecheck && npm run sync:check
# Docs
cd docs && npm ci && npm run build # fails on broken internal links

The worker suite covers the attribution ladder end to end (referrer, clipboard, Meta, scored fingerprint and confidence, late claims, idempotent replays, one install per click, synthesized clicks), the fraud rules and strict mode, Apple Search Ads against a stand-in API, OEM store routing and the tap-to-store page, request signing and rate limits, the events pipeline and customers, link and app CRUD, the redirect engine, retention and the schema registry.

To run the server locally with the demo account, follow Local quick start. To test an SDK change against it, run the worker with npm run dev:android and the example app with flutter run --dart-define-from-file=env/dev.json. See Testing.

  • apps/worker/migrations/ is append-only. A migration that has been applied to a shared or remote database is never edited or deleted; add a new numbered file.
  • Files apply in name order. 0001_schema.sql is the starting schema; each change after it is the next numbered file.
  • A migration that adds or removes a column must also change the row type in src/types.ts and the column registry in src/db/tables.ts. tsc enforces the type-registry pair and test/schema.test.ts compares the registry with PRAGMA table_info on the migrated test database.
  • Deploys run wrangler d1 migrations apply DB --remote --env production before wrangler deploy --env production. Plain npm run deploy uses the dev configuration. See Database and migrations.

The worker talks to D1 with raw, parameterized SQL and no ORM. The rules:

  1. Every value is bound with ?. Request input never reaches SQL text.
  2. Reads carry a row type from src/types.ts (.first<LinkRow>(), .all<AppRow>()) or an inline shape for aggregates. No any.
  3. The link, temp-link and user lookups live once, in src/db/links.ts, src/db/tempLinks.ts and src/db/users.ts. Do not paste that SQL again.
  4. Writes go through src/db/write.ts: insertStmt(db, table, row) requires the full row type, so a new column turns every insert site into a compile error until it supplies a value; updateById(db, table, id, patch) takes a typed partial. Expression updates such as click_count = click_count + 1, the analytics, attribution, rate-limit and cron SQL stay raw.
  5. Time cutoffs are computed in JavaScript (isoDaysAgo in src/util.ts) and bound as ISO strings. Never compare a stored timestamp with SQLite’s datetime('now', …): it emits YYYY-MM-DD HH:MM:SS, and because T sorts after a space, stored …T…Z values compare as newer than they are.
  • No billing. There is no billing or subscription code anywhere, and the docs do not describe any.
  • API keys are stored as a SHA-256 hash plus a prefix. The account key is shown once and kept no other way; SDK and site keys also keep an AES-GCM encrypted copy (keyed from JWT_SECRET) so Settings can show them again. Every SDK call is also HMAC-signed with the key.
  • Attribution: deterministic signals first (store referrers, Meta, pasteboard), then a scored, tenant-scoped fingerprint; one install per click; 24-hour window for candidates with a device model or a screen profile to compare with the install and 2 hours otherwise; credit only at high confidence, routing at medium, nothing on a tie between different links. See Attribution internals.
  • Privacy: hashed IPs (IPv6 at /64), raw rows deleted (or their personal columns blanked) on the retention schedule, ad identifiers in their own table and never joined to device data, and a vendor-scoped device id that survives a reinstall by default (persistDeviceId on iOS). See Privacy and data.
  • The name: DecTog is a coined word with no expansion. Prose writes DecTog, one word with a capital D and a capital T; code, package names and domains use dectog; identifiers keep their language’s casing (FlutterDectog, DectogConfig).
  1. Open an issue on the repository the change belongs to. For behavior changes, describe the current behavior and the one you want before writing code.
  2. Fork, branch, and keep the checks above green. A worker change that touches a route, a verdict field or a limit needs a test in apps/worker/test/; an SDK change needs one in test/.
  3. Keep the prose in step with the code. The pages of this site, docs/API_CONTRACT.md, apps/web/public/SKILL.md and llms.txt all describe the same behavior; update the ones your change affects, and bump the version note in src/components/Footer.astro when the SDK version or the latest migration changes.
  4. Never commit secrets. .dev.vars, env/dev.json and OAuth client secret files stay out of commits.
  5. Open a pull request. The docs site builds every pull request; a merged change reaches docs.dectog.com with the next deploy.

Documentation fixes are the quickest contribution: every page has an “Edit page” link that opens the file on GitHub.

  • The server (DecTog/dectog: the Worker, the web app and everything else in that repository) is licensed under the GNU AGPL v3. You can run it, modify it and self-host it. If you offer a modified version as a network service, you must publish your changes.
  • The Flutter SDK (DecTog/flutter_dectog) is licensed under the MIT License, so the AGPL never reaches your app.
  • These docs: the content under src/content is licensed under CC BY 4.0 (reuse it with attribution); the site code (configuration, components, styles) is MIT.