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 |
Repository layout
Section titled “Repository layout”dectog (server)
Section titled “dectog (server)”| 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 |
flutter_dectog (SDK)
Section titled “flutter_dectog (SDK)”| 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) |
react-native-dectog (SDK)
Section titled “react-native-dectog (SDK)”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) |
docs (this site)
Section titled “docs (this site)”| 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 |
Running the checks
Section titled “Running the checks”# Servercd apps/worker && npm ci && npm test # Vitest on the Workers pool (on macOS: npm run test:split)cd apps/worker && npm run typecheck # tsc --noEmitcd apps/web && npm ci && npm run typecheck
# SDKscd flutter_dectog && flutter pub get && flutter analyze && flutter testcd react-native-dectog && npm ci && npm test && npm run typecheck && npm run sync:check
# Docscd docs && npm ci && npm run build # fails on broken internal linksThe 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.
Migration rules
Section titled “Migration rules”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.sqlis 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.tsand the column registry insrc/db/tables.ts.tscenforces the type-registry pair andtest/schema.test.tscompares the registry withPRAGMA table_infoon the migrated test database. - Deploys run
wrangler d1 migrations apply DB --remote --env productionbeforewrangler deploy --env production. Plainnpm run deployuses the dev configuration. See Database and migrations.
Data-layer rules
Section titled “Data-layer rules”The worker talks to D1 with raw, parameterized SQL and no ORM. The rules:
- Every value is bound with
?. Request input never reaches SQL text. - Reads carry a row type from
src/types.ts(.first<LinkRow>(),.all<AppRow>()) or an inline shape for aggregates. Noany. - The link, temp-link and user lookups live once, in
src/db/links.ts,src/db/tempLinks.tsandsrc/db/users.ts. Do not paste that SQL again. - 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 asclick_count = click_count + 1, the analytics, attribution, rate-limit and cron SQL stay raw. - Time cutoffs are computed in JavaScript (
isoDaysAgoinsrc/util.ts) and bound as ISO strings. Never compare a stored timestamp with SQLite’sdatetime('now', …): it emitsYYYY-MM-DD HH:MM:SS, and becauseTsorts after a space, stored…T…Zvalues compare as newer than they are.
Design decisions to keep
Section titled “Design decisions to keep”- 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 (
persistDeviceIdon 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).
Proposing a change
Section titled “Proposing a change”- 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.
- 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 intest/. - Keep the prose in step with the code. The pages of this site,
docs/API_CONTRACT.md,apps/web/public/SKILL.mdandllms.txtall describe the same behavior; update the ones your change affects, and bump the version note insrc/components/Footer.astrowhen the SDK version or the latest migration changes. - Never commit secrets.
.dev.vars,env/dev.jsonand OAuth client secret files stay out of commits. - 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.
Licenses
Section titled “Licenses”- 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/contentis licensed under CC BY 4.0 (reuse it with attribution); the site code (configuration, components, styles) is MIT.