feat(collector): Add Corp, Domain, and VisitorSalt entity models for cross-domain tracking

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
autocommit 2026-05-14 22:58:03 -07:00
parent 24cff48bff
commit 9ca4222c0d
4 changed files with 85 additions and 0 deletions

View file

@ -0,0 +1,25 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
} from 'typeorm';
/**
* Corp top-level brand/legal-entity grouping for cross-domain analytics.
* One corp owns 1..N domains. Cross-corp flow queries pivot on this.
*/
@Entity('corps')
export class Corp {
@PrimaryGeneratedColumn({ type: 'smallint' })
id!: number;
@Column({ type: 'varchar', length: 64, unique: true })
slug!: string;
@Column({ type: 'varchar', length: 255, name: 'legal_name' })
legalName!: string;
@CreateDateColumn({ type: 'timestamptz', name: 'created_at' })
createdAt!: Date;
}

View file

@ -0,0 +1,39 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
ManyToOne,
JoinColumn,
Index,
} from 'typeorm';
import { Corp } from './corp.entity';
export type DomainRole = 'canonical' | 'alias' | 'seo_bait' | 'preview';
/**
* Domain hostname registered against a corp. Used to dimension every event
* with corp_id + domain_id derived from the request Origin/Referer/Host.
*/
@Entity('domains')
@Index(['corpId'])
export class Domain {
@PrimaryGeneratedColumn()
id!: number;
@Column({ type: 'smallint', name: 'corp_id' })
corpId!: number;
@ManyToOne(() => Corp)
@JoinColumn({ name: 'corp_id' })
corp!: Corp;
@Column({ type: 'varchar', length: 255, unique: true })
hostname!: string;
@Column({ type: 'varchar', length: 16 })
role!: DomainRole;
@CreateDateColumn({ type: 'timestamptz', name: 'created_at' })
createdAt!: Date;
}

View file

@ -1,2 +1,6 @@
export { RawEvent } from './raw-event.entity';
export { SessionFingerprint } from './session-fingerprint.entity';
export { Corp } from "./corp.entity";
export { Domain } from "./domain.entity";
export type { DomainRole } from "./domain.entity";
export { VisitorSalt } from "./visitor-salt.entity";

View file

@ -0,0 +1,17 @@
import { Entity, PrimaryColumn, Column } from 'typeorm';
/**
* Daily-rotating salt for visitor_id_daily. One row per UTC day.
* Salts older than ~7 days are purged to make historical re-identification
* mathematically impossible (cookie-free, GDPR-clean).
*/
@Entity('visitor_salts')
export class VisitorSalt {
/** UTC date (YYYY-MM-DD). Stored as DATE, no time component. */
@PrimaryColumn({ type: 'date' })
day!: string;
/** 32 random bytes. */
@Column({ type: 'bytea' })
salt!: Buffer;
}