Database
1 min
\ commandit database schema (finalized) \ dialect postgresql \ notes simplified comments for brevity assumes necessary extensions and trigger functions exist \ ============================================= \ core platform & organization \ ============================================= create table organizationtypes ( org type id serial primary key, type name varchar(50) not null unique, e g , 'client', 'vendor', 'serviceprovider' description text ); create table contacts ( contact id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, site location id uuid references locations(location id) on delete set null, first name varchar(100), last name varchar(100), email primary varchar(255) unique, is email verified boolean default false, phone office varchar(50), phone mobile varchar(50), is mobile verified boolean default false, job title varchar(100), is primary boolean default false, is vip boolean default false, is authorized caller boolean default true, reports to contact id uuid null references contacts(contact id) on delete set null, security pin hash varchar(255), security question text, security answer hash varchar(255), sms consent given boolean not null default false, allow email notifications boolean not null default true, allow sms notifications boolean not null default false, external contact id varchar(50), created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint contact cannot report to self check (contact id != reports to contact id) ); create table verificationcodes ( code hash varchar(64) primary key, hash of the code sent contact id uuid null references contacts(contact id) on delete cascade, link if verifying a contact user id uuid null references users(user id) on delete cascade, link if verifying a user (e g , password reset) delivery method varchar(10) not null check (delivery method in ('sms', 'email', 'apppush')), how code was sent purpose varchar(50) default 'callerverification', what is this code for? e g , 'callerverification', 'passwordreset', 'mfasetup' expires at timestamptz not null, expiry time for the code created at timestamptz not null default current timestamp, used at timestamptz null, timestamp when the code was successfully used check (contact id is not null or user id is not null) must be linked to a person/account ); create table organizations ( org id uuid primary key default gen random uuid(), parent msp org id uuid null references organizations(org id) on delete set null, added link to managing msp org if this is a client org name varchar(255) not null unique, logo url text, data residency region varchar(50), supported country codes text\[] null, is active boolean not null default true, default currency code varchar(3) not null default 'cad', default business hours id uuid references businesshours(business hours id) on delete set null, default site location id uuid references locations(location id) on delete set null, default primary contact id uuid references contacts(contact id) on delete set null, default timezone varchar(100) null, pricing rule set id uuid null references pricingrulesets(rule set id) on delete set null, account manager user id uuid references users(user id) on delete set null, primary tech user id uuid references users(user id) on delete set null, alert processing policy id uuid null references alertprocessingpolicies(policy id) on delete set null; custom fields jsonb, created at timestamptz not null default current timestamp, updated at timestamptz ); comment on column organizations parent msp org id is 'if this organization is a client managed by an msp, this links to the msp''s org id '; comment on column organizations pricing rule set id is 'optional link to a specific pricingruleset that overrides the msp default for this client organization '; comment on column organizations default currency code is 'default iso 4217 currency code for financial transactions related to this organization (e g , cad, usd) '; comment on column organizations default timezone is 'default iana timezone name (e g , ''america/vancouver'') for the organization '; comment on column organizations supported country codes is 'for vendor org types, lists iso 3166 1 alpha 2 country codes where they operate/distribute '; comment on column organizations alert processing policy id is 'optional link to a specific alertprocessingpolicy that overrides inherited policies for alerts concerning this organization '; create table organizationtypeassignments ( org id uuid not null references organizations(org id) on delete cascade, org type id integer not null references organizationtypes(org type id) on delete restrict, assigned at timestamptz not null default current timestamp, primary key (org id, org type id) ); create table locations ( location id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(255) not null, location type varchar(20) not null check (location type in ('office', 'clientsite', 'datacenter', 'homebase', 'warehouse', 'virtual', 'remote', 'other')), is system defined boolean not null default false, parent location id uuid references locations(location id) on delete set null, street address varchar(255), city varchar(100), province state varchar(100), postal code varchar(20), country code varchar(2), latitude numeric(9, 6), longitude numeric(9, 6), time zone varchar(100), business hours id uuid null references businesshours(business hours id) on delete set null, zone id uuid references servicezones(zone id) on delete set null, pricing rule set id uuid null references pricingrulesets(rule set id) on delete set null, pricing rules specific to this location is primary office boolean not null default false, override rate sheet id uuid references ratesheets(rate sheet id) on delete set null, parking instructions text, entry instructions text, external site id varchar(50), alert processing policy id uuid null references alertprocessingpolicies(policy id) on delete set null; created at timestamptz not null default current timestamp, updated at timestamptz, auto updated by trigger constraint locations no self parent check (location id != parent location id), constraint locations org name unique unique(org id, name) ); comment on column locations pricing rule set id is 'optional link to a specific pricingruleset that overrides org/msp defaults for this location '; comment on column locations time zone is 'iana timezone name (e g , ''america/vancouver'') used for local time calculations if business hours type is locationlocaltime '; comment on column locations business hours id is 'optional business hours specific to this location if null, inherits default business hours from the parent organization '; comment on column locations alert processing policy id is 'optional link to a specific alertprocessingpolicy that overrides inherited policies for alerts concerning this location '; create table users ( user id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, contact id uuid null unique references contacts(contact id) on delete set null, email varchar(255) not null unique, hashed password varchar(255), first name varchar(100), last name varchar(100), employment type varchar(20) default 'fulltime' check (employment type in ('fulltime', 'parttime', 'contractor', 'vendoruser')), avatar url text, home base location id uuid references locations(location id) on delete set null, assigned office location id uuid references locations(location id) on delete set null, location id uuid references locations(location id) on delete set null, preferred language varchar(10) not null default 'en', is active boolean not null default true, is portal access allowed boolean not null default true, is anonymized boolean not null default false, is ai boolean not null default false, sp ai override id uuid references spaiagentoverrides(override id) on delete set null, default work type id uuid references worktypes(work type id) on delete set null, job title varchar(255), phone number varchar(50), is mobile verified for login boolean default false, default start address type varchar(10) default 'office' check (default start address type in ('office', 'homebase', 'lastknown')), current status enum varchar(20) check (current status enum in ('available', 'travelingtosite', 'onsite', 'lunch', 'meeting', 'offduty', 'vacation', 'away', 'idle')), manual or inferred status current latitude numeric(9, 6), current longitude numeric(9, 6), last location update timestamp timestamptz, current schedule entry id bigint references technicianschedules(schedule entry id) on delete set null, current eta timestamp timestamptz, last status change timestamp timestamptz, last system activity timestamp timestamptz null, last interaction with any commandit interface current session start timestamp timestamptz null, when the current login session began current session source varchar(20) null check (current session source in ('web', 'agent', 'mobile', 'api')), how user is connected location tracking enabled boolean default true, mobile device push token text, external auth provider varchar(50), external auth user id varchar(255), latest nps score integer check (latest nps score >= 0 and latest nps score <= 10), latest nps submitted at timestamptz, last login timestamptz, still useful for last successful login overall custom fields jsonb, created at timestamptz not null default current timestamp, updated at timestamptz, check ((is ai = false and sp ai override id is null) or (is ai = true)), constraint users external auth unique unique (external auth provider, external auth user id) ); comment on column users last system activity timestamp is 'timestamp of the last detected user interaction with any commandit system component (web, agent, mobile, api) used to infer idle status '; comment on column users current session start timestamp is 'timestamp when the user''s current login session started null if logged out '; comment on column users current session source is 'indicates the primary interface used to establish the current login session '; create table usersessions ( user session id uuid primary key default gen random uuid(), unique id for the session user id uuid not null references users(user id) on delete cascade, session start time timestamptz not null default current timestamp, when the session was initiated session end time timestamptz null, when the session ended (null if currently active) source type varchar(20) not null check (source type in ('web', 'agent', 'mobile', 'api', 'unknown')), how the session was initiated source ip address inet null, ip address at the start of the session source geo location jsonb null, geoip lookup for the starting ip user agent text null, user agent string from the client last activity timestamp timestamptz null, timestamp of the last known activity within this session logout reason varchar(30) null check (logout reason in ('userlogout', 'timeout', 'forcedadmin', 'systemshutdown')), why the session ended created at timestamptz not null default current timestamp \ add index on user id, session start time \ add index on user id where session end time is null (for finding active sessions) ); comment on table usersessions is 'tracks user login sessions, their duration, source, and last activity within the session '; comment on column usersessions session end time is 'timestamp when the session ended null indicates the session is currently active '; comment on column usersessions last activity timestamp is 'timestamp of the last detected user interaction associated with this specific session '; comment on column usersessions logout reason is 'reason why the session ended (user action, inactivity timeout, admin action, etc ) '; create table roles ( role id uuid primary key default gen random uuid(), org id uuid references organizations(org id) on delete cascade, null for system roles name varchar(100) not null, description text, is system boolean not null default false, non editable system role is portal role boolean not null default false, is this role intended for portal users? created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint roles org name unique unique nulls not distinct (org id, name) ); create table userroleassignments ( user id uuid not null references users(user id) on delete cascade, role id uuid not null references roles(role id) on delete cascade, assigned at timestamptz not null default current timestamp, primary key (user id, role id) ); create table rolepermissions ( permission id bigserial primary key, role id uuid not null references roles(role id) on delete cascade, permission key varchar(255) not null, portal link url varchar(512), portal link title varchar(100), unique(role id, permission key) ); create table teams ( team id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(100) not null, description text, is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique(org id, name) ); create table userteamassignments ( user id uuid not null references users(user id) on delete cascade, team id uuid not null references teams(team id) on delete cascade, is primary team boolean default false, assigned at timestamptz not null default current timestamp, primary key (user id, team id) ); create table tags ( tag id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, tag key varchar(100) not null, tag value varchar(255) not null, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint tags org key value unique unique(org id, tag key, tag value) ); create table usertags ( user id uuid not null references users(user id) on delete cascade, tag id uuid not null references tags(tag id) on delete cascade, primary key (user id, tag id) ); create table useremails ( user email id bigserial primary key, user id uuid not null references users(user id) on delete cascade, email address varchar(255) not null, source varchar(50) not null, is monitored boolean not null default true, constraint useremails user email unique unique(user id, email address) ); \ maps internet domains to organizations create table organizationdomains ( org domain id bigserial primary key, org id uuid not null references organizations(org id) on delete cascade, domain name varchar(255) not null, e g , 'example com' is verified boolean not null default false, verification method varchar(20) null, verified at timestamptz null, notes text null, added at timestamptz not null default current timestamp, added by user id uuid null references users(user id) on delete set null ); create index idx orgdomains domain name on organizationdomains(domain name); create index idx orgdomains org id on organizationdomains(org id); alter table organizationdomains add constraint orgdomains org domain unique unique (org id, domain name); comment on table organizationdomains is 'maps internet domain names to specific organizations within commandit '; \ ============================================= \ configuration management (cmdb) \ ============================================= create table manufacturers ( manufacturer id uuid primary key default gen random uuid(), name varchar(255) not null unique, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); \ main table for devices managed by commandit create table devices ( device id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(255) not null, smbios uuid uuid null, device type varchar(100) not null, e g , 'server', 'workstation', 'laptop', 'cloudserver', 'networkdevice', 'mobile', 'pbx', 'voipsystem', 'ups', 'pdu', 'hvac', 'environmentalsensor' is virtual boolean not null default false, status varchar(50) not null default 'active' check (status in ('active', 'inactive', 'inrepair', 'disposed', 'archived', 'lost', 'stolen')), location id uuid references locations(location id) on delete set null, primary contact user id uuid references users(user id) on delete set null, manufacturer id uuid references manufacturers(manufacturer id) on delete set null, model number varchar(255), mfg part number varchar(255), serial number varchar(255), asset tag varchar(100), note server side set, but field exists for manual entry/import imei varchar(20), meid varchar(18), iccid varchar(22), cpu architecture varchar(50), agent version varchar(50) null, agent service version varchar(50) null, agent ui version varchar(50) null, agent probe version varchar(50) null, agent updater version varchar(50) null, agent last full sync timestamptz null, agent health status varchar(20) null, agent last health check timestamptz null, agent health details text null, is probe boolean default false, last agent checkin timestamptz null, inventory detail level varchar(20) not null default 'normal', enhanced mode expires at timestamptz, install date timestamptz, os install date installed by user id uuid references users(user id) on delete set null, vendor org id uuid references organizations(org id) on delete set null, purchase date date, warranty expiry date date, warranty provider varchar(255), warranty status varchar(100), last warranty check timestamp timestamptz, warranty details url text, custom sla id uuid references slas(sla id) on delete set null, manual replacement date date, ip address inet, external ip address inet, default gateway inet, os type varchar(50), os version varchar(100), os name varchar(255), is reboot pending boolean null, added flag indicating if the os reports a reboot is pending rack id uuid null references racks(rack id) on delete set null, rack position u integer null, starting u position (bottom u of the device) rack units consumed integer null default 1 check (rack units consumed is null or rack units consumed >= 1), how many u's the device occupies btu output standard integer null, standard/typical thermal output in btu/hour btu output peak integer null, peak thermal output in btu/hour power consumption watts standard integer null, standard/typical power consumption in watts power consumption watts peak integer null, peak power consumption in watts power draw amps peak numeric(6, 2) null, peak amperage draw last logged in user varchar(255), last reported latitude numeric(9, 6) null, last reported longitude numeric(9, 6) null, last location accuracy meters integer null, added accuracy radius in meters last location source varchar(30) null check (last location source in ('os gps', 'os wifi', 'os network', 'ip geolocation', 'unknown')), added source method last location report timestamp timestamptz null, windows11 compatibility status varchar(20) check (windows11 compatibility status in ('compatible', 'notcompatible', 'unknown', 'checkfailed')), added windows11 incompatibility reasons text\[] null, added windows11 last check time timestamptz null, added configuration jsonb, store tpm, uefi status, cloud vm details, pbx details, etc here created at timestamptz not null default current timestamp, updated at timestamptz, \ constraints constraint devices org serial unique unique nulls not distinct (org id, serial number), constraint devices org asset tag unique unique nulls not distinct (org id, asset tag) ); comment on column devices install date is 'operating system installation date, used for aging reports ensure agent populates this reliably '; comment on column devices is reboot pending is 'flag indicating if the os reports a reboot is pending (e g , after updates) collected by agent '; comment on column devices last reported latitude is 'last reported latitude from os location services '; comment on column devices last reported longitude is 'last reported longitude from os location services '; comment on column devices last location accuracy meters is 'estimated accuracy radius in meters of the last location report '; comment on column devices last location source is 'method used to obtain the last reported location (os api, ip geolocation) '; comment on column devices last location report timestamp is 'timestamp when the last location was successfully reported by the agent or derived server side '; comment on column devices windows11 compatibility status is 'assessed compatibility status with windows 11 (calculated server side) '; comment on column devices windows11 incompatibility reasons is 'array listing reasons for windows 11 incompatibility (calculated server side) '; comment on column devices windows11 last check time is 'timestamp when windows 11 compatibility was last assessed by the server '; comment on column devices configuration is 'jsonb field storing device specific config (tpm/uefi status, cloud vm details, pbx details, etc ) and potentially less critical os state flags '; comment on column devices rack id is 'fk linking the device to the specific rack it is installed in '; comment on column devices rack position u is 'the bottom most u position occupied by this device within the rack '; comment on column devices rack units consumed is 'the number of u units this device occupies in the rack (defaults to 1 if not specified) '; comment on column devices btu output standard is 'rated or typical thermal output specification in btu/hour '; comment on column devices btu output peak is 'maximum rated thermal output specification in btu/hour '; comment on column devices power consumption watts standard is 'rated or typical operational power consumption specification in watts '; comment on column devices power consumption watts peak is 'maximum rated power consumption specification in watts '; comment on column devices power draw amps peak is 'maximum rated amperage draw specification '; comment on column devices updated at is 'timestamp of the last update to this record, typically updated automatically by agent check ins or manual modifications '; create index idx devices org id on devices(org id); create index idx devices location id on devices(location id); create index idx devices os name on devices(os name); create index idx devices device type on devices(device type); create index idx devices last checkin on devices(last agent checkin); create table devicetags ( device id uuid not null references devices(device id) on delete cascade, tag id uuid not null references tags(tag id) on delete cascade, primary key (device id, tag id) ); create table devicehardware ( device id uuid primary key references devices(device id) on delete cascade, cpu details jsonb null, incl model, speed, cores, logical, arch, virt flags etc ram total mb integer check (ram total mb >= 0), ram slots used integer null, ram slots total integer null, ram details jsonb null, array slot, capacity, type, speed, mfg, part#, serial# bios info jsonb null, incl manufacturer, version, release date, secure boot status etc motherboard info jsonb null, incl manufacturer, product, serial# chassis info jsonb null, incl manufacturer, type, serial# video controllers jsonb null, array name, mfg, ram, driver version, wddm/directx level sound devices jsonb null, array name, mfg usb controllers jsonb null, added array storing details about detected usb controllers tpm details jsonb null, added structured tpm details (presence, enabled, version, manufacturer id/version) last updated at timestamptz timestamp when this hardware inventory data was last collected/updated ); comment on table devicehardware is 'stores detailed hardware component information (cpu, ram, bios, motherboard, chassis, graphics, sound, usb controllers, tpm) for a device '; comment on column devicehardware tpm details is 'jsonb storing structured tpm details (presence, enabled, specversion, manufacturerversion, manufacturerid) '; comment on column devicehardware cpu details is 'jsonb containing detailed cpu information (model, speed, cores, logical processors, architecture, features) '; comment on column devicehardware ram details is 'jsonb array storing details for each installed ram module (slot, capacity, type, speed, manufacturer, part#, serial#) '; comment on column devicehardware bios info is 'jsonb containing bios/firmware details (manufacturer, version, release date, secure boot status) '; comment on column devicehardware motherboard info is 'jsonb containing motherboard details (manufacturer, product, serial#) '; comment on column devicehardware chassis info is 'jsonb containing chassis details (manufacturer, type, serial#) '; comment on column devicehardware video controllers is 'jsonb array storing details for each video controller (name, manufacturer, ram, driver version, wddm/directx info) '; comment on column devicehardware sound devices is 'jsonb array storing details for each sound device (name, manufacturer) '; comment on column devicehardware usb controllers is 'jsonb array storing details about detected usb controllers '; create table applications ( application id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, the primary org associated with this application name varchar(255) not null, user friendly name of the application description text, application type varchar(50) null check (application type in ('commercialofftheshelf', 'customdeveloped', 'saas', 'internal', 'other')), business owner contact id uuid null references contacts(contact id) on delete set null, primary business contact/owner technical owner user id uuid null references users(user id) on delete set null, primary technical contact/owner (msp or client user) criticality varchar(20) null check (criticality in ('veryhigh', 'high', 'medium', 'low', 'informational')), business criticality status varchar(20) default 'production' check (status in ('planning', 'development', 'testing', 'production', 'pilot', 'retired')), vendor org id uuid null references organizations(org id) on delete set null, vendor if cots/saas version varchar(100) null, current version if applicable url text null, primary url for accessing the application (if web based) notes text, use polymorphic notes table instead? keep simple notes here for now created at timestamptz not null default current timestamp, updated at timestamptz, auto updated by trigger unique (org id, name) ); comment on table applications is 'represents logical business applications, potentially composed of multiple infrastructure cis '; comment on column applications criticality is 'business criticality level assigned to the application '; create table peripherals ( peripheral id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, peripheral type varchar(50) not null check (peripheral type in ('monitor', 'keyboard', 'mouse', 'dockingstation', 'webcam', 'other')), manufacturer id uuid references manufacturers(manufacturer id) on delete set null, model varchar(255), serial number varchar(255), asset tag varchar(100), status varchar(30) default 'inuse' check (status in ('instock', 'inuse', 'inrepair', 'retired', 'missing')), purchase date date, warranty expiry date date, location id uuid references locations(location id) on delete set null, assigned user id uuid references users(user id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint peripherals org serial unique unique nulls not distinct (org id, serial number), constraint peripherals org asset tag unique unique nulls not distinct (org id, asset tag) ); create table deviceperipherals ( device id uuid not null references devices(device id) on delete cascade, peripheral id uuid not null references peripherals(peripheral id) on delete cascade, connection type varchar(50), assigned at timestamptz not null default current timestamp, primary key (device id, peripheral id) ); create table physicaldisks ( physical disk id uuid primary key default gen random uuid(), device id uuid not null references devices(device id) on delete cascade, manufacturer id uuid references manufacturers(manufacturer id) on delete set null, model varchar(255), serial number varchar(255) unique, firmware version varchar(50), interface type varchar(20) check (interface type in ('sata', 'sas', 'nvme', 'usb', 'scsi', 'fc', 'ide', 'other')), media type varchar(20) check (media type in ('hdd', 'ssd', 'nvme ssd', 'removable', 'unknown')), capacity bytes bigint check (capacity bytes >= 0), status varchar(30) default 'online' check (status in ('online', 'offline', 'failed', 'predictivefailure', 'rebuilding', 'spare', 'unconfigured', 'missing', 'unknown')), includes smart status mapping physical location varchar(100), e g , disk 0, slot 1 power on hours integer null, added disk power on hours storage controller device id uuid references devices(device id) on delete set null, storage array id uuid references storagearrays(storage array id) on delete set null, raid role varchar(20) check (raid role in ('member', 'hotspare', 'globalspare', 'parity', 'cache', 'journal')), last updated at timestamptz not null default current timestamp ); comment on table physicaldisks is 'stores details about physical disk drives in devices, including smart status and power on hours '; comment on column physicaldisks status is 'operational status, potentially including mapped smart health status (e g , online, failed, predictivefailure) '; comment on column physicaldisks power on hours is 'total hours the disk has been powered on, typically retrieved from smart data '; create index idx physicaldisks device id on physicaldisks(device id); create table storagearrays ( storage array id uuid primary key default gen random uuid(), controller device id uuid not null references devices(device id) on delete cascade, name varchar(255) not null, raid level varchar(50), status varchar(30) default 'optimal' check (status in ('optimal', 'degraded', 'rebuilding', 'failed', 'offline')), capacity bytes bigint check (capacity bytes >= 0), used bytes bigint check (used bytes >= 0), notes text, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique(controller device id, name) ); create table softwareproducts ( software product id uuid primary key default gen random uuid(), manufacturer id uuid references manufacturers(manufacturer id) on delete set null, name varchar(255) not null, version varchar(100), edition varchar(100), platform varchar(50) default 'crossplatform', category varchar(100), end of life date date, end of support date date, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint softwareproducts unique unique nulls not distinct (manufacturer id, name, version, edition, platform) ); create table softwarenormalizationrules ( rule id uuid primary key default gen random uuid(), org id uuid references organizations(org id) on delete cascade, description varchar(255), match criteria jsonb not null, target software product id uuid not null references softwareproducts(software product id) on delete cascade, priority integer not null default 100, is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table devicesoftware ( device software id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, display name varchar(255) not null, version varchar(100), publisher varchar(255), install date date, install path text, detected software product id uuid references softwareproducts(software product id) on delete set null, normalized product last detected at timestamptz not null default current timestamp, source varchar(50), how was this record created/updated (agent, manual, import) \ agent detected licensing info detected license key text null, license key found on the device by agent (may be generic kms/mak, oem marker, etc ) detected activation status varchar(30) null check (detected activation status in ('activated', 'graceperiod', 'notification', 'unlicensed', 'oobgrace', 'ootgrace', 'nongenuinegrace', 'unknown')), activation status reported by the agent constraint devicesoftware unique unique nulls not distinct (device id, display name, version, publisher) ); comment on table devicesoftware is 'tracks software installations detected on devices '; comment on column devicesoftware detected license key is 'raw license key detected on the device for this software installation (informational, may not be entitlement key) '; comment on column devicesoftware detected activation status is 'activation status reported by the agent for this installation (e g , wmi query result) '; create table devicelogicaldisks ( disk id uuid primary key default gen random uuid(), device id uuid not null references devices(device id) on delete cascade, drive letter or mount varchar(255) not null, volume name varchar(255), file system varchar(50), size bytes bigint check (size bytes >= 0), free space bytes bigint check (free space bytes >= 0), is os drive boolean null, added flag indicating if this volume holds the os parent type varchar(20) null check (parent type in ('physicaldisk', 'storagearray', 'softwareraid', 'unknown')), added type of underlying storage parent identifiers text\[] null, added array of identifiers for the underlying storage last updated at timestamptz not null default current timestamp, unique(device id, drive letter or mount) ); comment on table devicelogicaldisks is 'stores information about logical disks (volumes/partitions) on a device, including os drive flag and link to underlying physical storage '; comment on column devicelogicaldisks is os drive is 'flag indicating if this logical disk contains the primary operating system '; comment on column devicelogicaldisks parent type is 'type of underlying storage (physicaldisk, storagearray, softwareraid) '; comment on column devicelogicaldisks parent identifiers is 'array of identifiers (e g , physical disk uuids/serials, storage array uuid) for the underlying parent storage '; create index idx devicelogicaldisks device id on devicelogicaldisks(device id); create index idx devicelogicaldisks osdrive on devicelogicaldisks(device id, is os drive) where is os drive = true; create table deviceservices ( service entry id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, service name varchar(255) not null, display name varchar(255), status varchar(50), start type varchar(50), path name text, log on as varchar(255), last updated at timestamptz not null default current timestamp, unique(device id, service name) ); create table deviceprocesses ( device process id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, snapshot timestamp timestamptz not null default current timestamp, process name varchar(255) not null, pid integer not null, cpu usage percent numeric(5,2), memory usage bytes bigint, user name varchar(255), path text, command line text, start time timestamptz ); create table equipmenttypes ( equipment type id serial primary key, org id uuid not null references organizations(org id) on delete cascade, name varchar(100) not null, description text, is individually tracked boolean not null default false, unique (org id, name) ); create table equipmentassets ( equipment asset id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, equipment type id integer not null references equipmenttypes(equipment type id) on delete restrict, asset tag varchar(100) unique, serial number varchar(255) unique nulls not distinct, name varchar(255), description text, manufacturer id uuid references manufacturers(manufacturer id) on delete set null, model varchar(100), purchase date date, warranty expiry date date, current status varchar(20) default 'available' check (current status in ('available', 'assigned', 'inrepair', 'retired', 'missing')), current location id uuid references locations(location id) on delete set null, assigned technician user id uuid references users(user id) on delete set null, last seen at timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table serializedassetinstances ( serial instance id bigint primary key references receiveditemserials(serial instance id) on delete cascade, product id uuid not null references products(product id) on delete restrict, serial number varchar(255) not null unique, current status varchar(30) not null default 'instock' check (current status in ('instock', 'allocated', 'deployed', 'inrepair', 'disposed', 'returnedtovendor', 'missing')), current location id uuid references locations(location id) on delete set null, assigned device id uuid references devices(device id) on delete set null, assigned user id uuid references users(user id) on delete set null, warranty expiry date date, purchase cost numeric(19,4), last status update timestamptz not null default current timestamp ); create table configurationitemrelationships ( relationship id bigserial primary key, parent ci type varchar(50) not null, parent ci id varchar(36) not null, child ci type varchar(50) not null, child ci id varchar(36) not null, relationship type varchar(50) not null, description text, created at timestamptz not null default current timestamp, unique (parent ci type, parent ci id, child ci type, child ci id, relationship type) ); create table attachments ( attachment id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, file name varchar(255) not null, mime type varchar(100) not null, file size bytes bigint not null, storage provider varchar(50) not null default 'localstorage', storage path text not null unique, uploaded by user id uuid references users(user id) on delete set null, created at timestamptz not null default current timestamp ); create table locationattachments ( location id uuid not null references locations(location id) on delete cascade, attachment id uuid not null references attachments(attachment id) on delete cascade, description text, is primary image boolean not null default false, attached at timestamptz not null default current timestamp, primary key (location id, attachment id) ); create table deviceattachments ( device id uuid not null references devices(device id) on delete cascade, attachment id uuid not null references attachments(attachment id) on delete cascade, description text, is primary image boolean not null default false, attached at timestamptz not null default current timestamp, primary key (device id, attachment id) ); \ ============================================= \ network infrastructure & topology \ ============================================= create table networkdeviceports ( port id uuid primary key default gen random uuid(), device id uuid not null references devices(device id) on delete cascade, port index integer, port name varchar(255) not null, port description varchar(512), port type varchar(50) not null, e g , 'ethernet', 'fastethernet', 'gigabitethernet', 'serial', 'loopback' status varchar(50), operational status (e g , 'up', 'down', 'testing') admin status varchar(50), administrative status (e g , 'up', 'down') native vlan record id uuid null references vlans(vlan record id) on delete set null, fk to vlans record for the native/untagged vlan allowed vlan ids integer\[], array of numeric vlan ids allowed if this is a trunk port (1 4094) is trunk boolean, duplex varchar(10), 'full', 'half', 'auto' speed mbps bigint, is poe enabled boolean, is manually added boolean not null default false, last status change timestamptz, last updated at timestamptz not null default current timestamp, constraint networkdeviceports device port name unique unique(device id, port name) ); comment on table networkdeviceports is 'represents physical or logical ports on network devices (switches, routers) '; comment on column networkdeviceports native vlan record id is 'fk to the vlans table representing the native (untagged) vlan for this port '; comment on column networkdeviceports allowed vlan ids is 'array of integer vlan ids (1 4094) permitted on this port if it is configured as a trunk '; create table devicenetworkadapters ( adapter id uuid primary key default gen random uuid(), device id uuid not null references devices(device id) on delete cascade, name varchar(255) not null, description varchar(255), mac address macaddr, manufacturer varchar(255) null, added manufacturer status varchar(50), e g , 'up', 'down', 'disconnected' is physical boolean, is virtual boolean, vlan record id uuid null references vlans(vlan record id) on delete set null, ip addresses inet\[], subnets cidr\[], gateways inet\[], dns servers inet\[], dhcp enabled boolean, dhcp server inet, link speed mbps bigint, last updated at timestamptz not null default current timestamp, unique(device id, name), unique(device id, mac address) where mac address is not null ); comment on table devicenetworkadapters is 'represents network interface controllers (nics) on endpoint devices '; comment on column devicenetworkadapters manufacturer is 'manufacturer of the network adapter hardware '; create index idx devicenetworkadapters device id on devicenetworkadapters(device id); create index idx devicenetworkadapters mac address on devicenetworkadapters(mac address) where mac address is not null; create table networkconnections ( connection id bigserial primary key, local port id uuid not null references networkdeviceports(port id) on delete cascade, remote device name varchar(255), remote port name varchar(255), remote mac address macaddr, remote ip address inet, remote platform varchar(255), remote device id uuid references devices(device id) on delete set null, remote network adapter id uuid references devicenetworkadapters(adapter id) on delete set null, remote port id uuid references networkdeviceports(port id) on delete set null, connection type varchar(20) not null, discovery protocol varchar(20), status varchar(20) default 'active', last seen at timestamptz not null default current timestamp, first seen at timestamptz \ unique constraint on (local port id, remote mac address, remote port name) handled by index if needed ); create table wirelessnetworks ( wireless network id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, location id uuid references locations(location id) on delete set null, ssid name varchar(255) not null, security type varchar(50), authentication type varchar(50), encryption type varchar(50), is guest network boolean default false, configuration jsonb, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (org id, ssid name) ); create table snmpmonitoreddevices ( device id uuid primary key references devices(device id) on delete cascade, snmp version varchar(3) not null check (snmp version in ('v1', 'v2c', 'v3')), port integer not null default 161, community string text, encrypt or vault reference v3 security name varchar(100), v3 security level varchar(20) check (v3 security level in ('noauthnopriv', 'authnopriv', 'authpriv')), v3 auth protocol varchar(10) check (v3 auth protocol in ('md5', 'sha', 'sha256', 'sha384', 'sha512')), v3 auth passphrase text, encrypt or vault reference v3 priv protocol varchar(10) check (v3 priv protocol in ('des', 'aes', 'aes192', 'aes256')), v3 priv passphrase text, encrypt or vault reference timeout ms integer not null default 5000, retries integer not null default 2, last successful poll timestamptz, last error text, updated at timestamptz auto updated ); create table servicezones ( zone id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, owning org (msp) name varchar(100) not null, geo boundary geography(polygon, 4326), requires postgis extension description text, unique (org id, name) ); create table technicianservicezones ( technician user id uuid not null references users(user id) on delete cascade, zone id uuid not null references servicezones(zone id) on delete cascade, primary key (technician user id, zone id) ); create table vlans ( vlan record id uuid primary key default gen random uuid(), new stable primary key org id uuid not null references organizations(org id) on delete cascade, org defining/using this vlan vlan id integer not null check (vlan id >= 1 and vlan id <= 4094), the actual vlan id (1 4094) name varchar(100) null, optional descriptive name (e g , 'servers', 'workstations', 'voice') description text, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated by trigger constraint vlans org vlan id unique unique (org id, vlan id) vlan id must be unique within an organization ); comment on table vlans is 'represents virtual local area networks (vlans) uses a surrogate uuid key (vlan record id) for stable fk references '; comment on column vlans vlan record id is 'internal unique identifier for the vlan record '; comment on column vlans vlan id is 'the actual 802 1q vlan tag id (1 4094), unique within the scope of the owning organization '; comment on table vlans is 'represents virtual local area networks (vlans) '; create table subnets ( subnet id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, owning org subnet cidr cidr not null, subnet definition (e g , '192 168 1 0/24') name varchar(100), user friendly name (e g , 'main office lan', 'server subnet dc1') description text, location id uuid null references locations(location id) on delete set null, physical location association vlan record id uuid null references vlans(vlan record id) on delete set null, associated vlan record (fk) gateway ip inet null, default gateway for this subnet dhcp server ip inet null, primary dhcp server address dns server ips inet\[] null, array of dns server ips \ cloud network links (optional, if subnet represents a cloud vpc/subnet) aws vpc id varchar(50) null, aws vpc id if applicable aws subnet id varchar(50) null, aws subnet id if applicable azure vnet name varchar(255) null, azure vnet name if applicable azure subnet name varchar(255) null, azure subnet name if applicable gcp network name varchar(255) null, gcp network name if applicable gcp subnetwork name varchar(255) null, gcp subnetwork name if applicable is routable boolean default true, is this subnet expected to be routable? is active boolean default true, last scan time timestamptz, when ips in this subnet were last actively scanned created at timestamptz not null default current timestamp, updated at timestamptz, unique (org id, subnet cidr) ); comment on table subnets is 'defines network subnets, their properties, and associations '; comment on column subnets vlan record id is 'fk to the vlans table, linking this subnet to a specific vlan definition '; comment on column subnets location id is 'primary physical location associated with this subnet '; create table ipaddresses ( ip address id bigserial primary key, subnet id uuid not null references subnets(subnet id) on delete cascade, ip address inet not null, the actual ip address status varchar(20) not null check (status in ('static', 'dhcp', 'reserved', 'unused', 'conflict', 'transient')), usage type varchar(30) null check (usage type in ('server', 'workstation', 'printer', 'networkdevice', 'voipdevice', 'gateway', 'dhcpserver', 'dnsserver', 'management', 'vm', 'container', 'other')), what kind of device uses this ip? assigned device id uuid null references devices(device id) on delete set null, link to the device currently using this ip (if known/static) assigned adapter id uuid null references devicenetworkadapters(adapter id) on delete set null, link to specific nic (more transient for dhcp) hostname varchar(255) null, last known hostname associated with this ip (via dns/scan) mac address macaddr null, last known mac address associated with this ip last seen timestamp timestamptz null, when this ip was last detected as active/assigned first seen timestamp timestamptz null, notes text, use polymorphic notes table instead is critical boolean default false, flag critical ips like gateways, servers created at timestamptz not null default current timestamp, updated at timestamptz, unique (subnet id, ip address) an ip can only exist once within a given subnet definition \ add index on ip address for fast lookups across subnets if needed \ add index on assigned device id ); comment on table ipaddresses is 'tracks individual ip addresses within defined subnets, their status, and associations '; comment on column ipaddresses status is 'current assignment status (static, dhcp, reserved, unused, conflict, transient) '; comment on column ipaddresses usage type is 'intended or detected purpose of the device using this ip '; \ modify networkdeviceports and devicenetworkadapters to reference vlans alter table networkdeviceports add column native vlan id integer null; no fk if vlans pk isn't globally unique \ alter table networkdeviceports add column allowed vlan ids integer\[] null; already exists alter table devicenetworkadapters add column vlan id integer null; no fk if vlans pk isn't globally unique \ ============================================= \ policies & configuration management \ ============================================= create table scripts ( script id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(255) not null, description text, script language varchar(50) not null, script content text not null, execution timeout seconds integer not null default 300, is system defined boolean not null default false, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint scripts org name unique unique(org id, name) ); create table complianceframeworks ( framework id uuid primary key default gen random uuid(), name varchar(255) not null unique, version varchar(50), description text, url text, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table complianceframeworkcontrols ( control id uuid primary key default gen random uuid(), framework id uuid not null references complianceframeworks(framework id) on delete cascade, control identifier varchar(100) not null, control name varchar(512), control description text, url text, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique(framework id, control identifier) ); create table cwedetails ( common weakness enumeration lookup cwe id varchar(20) primary key, name varchar(255) not null, description text, url text, last synced at timestamptz ); create table compliancerules ( rule id uuid primary key default gen random uuid(), definer org id uuid references organizations(org id) on delete cascade, name varchar(255) not null, description text not null, platform os type varchar(50) not null default 'any', check logic type varchar(50) not null, check logic data jsonb not null, default check interval seconds integer not null check (default check interval seconds > 0), default remediation type varchar(50) not null default 'none', default remediation script id uuid references scripts(script id) on delete set null, default remediation ticket template id uuid references tickettemplates(template id) on delete set null, default remediation variables jsonb, is system defined boolean not null default false, is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint compliancerules org name unique unique nulls not distinct (definer org id, name) ); create table complianceruleframeworkmappings ( rule id uuid not null references compliancerules(rule id) on delete cascade, control id uuid not null references complianceframeworkcontrols(control id) on delete cascade, primary key (rule id, control id) ); create table compliancerulecwes ( rule id uuid not null references compliancerules(rule id) on delete cascade, cwe id varchar(20) not null references cwedetails(cwe id) on delete cascade, primary key (rule id, cwe id) ); create table compliancepolicies ( policy id uuid primary key default gen random uuid(), definer org id uuid not null references organizations(org id) on delete cascade, name varchar(255) not null, description text, category varchar(100), policy data jsonb not null, is enabled boolean not null default true, scope type varchar(20) not null default 'organization', scope id uuid, parent policy id uuid references compliancepolicies(policy id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint compliancepolicies org name scope unique unique(definer org id, name, scope type, scope id) ); create table tagcompliancepolicyassignments ( assignment id bigserial primary key, tag id uuid not null references tags(tag id) on delete cascade, compliance policy id uuid not null references compliancepolicies(policy id) on delete cascade, is enabled boolean not null default true, priority integer not null default 0, assigned at timestamptz not null default current timestamp ); create table patchpolicies ( policy id uuid primary key default gen random uuid(), definer org id uuid not null references organizations(org id) on delete cascade, name varchar(255) not null, description text, category varchar(100), policy data jsonb not null, is enabled boolean not null default true, scope type varchar(20) not null default 'organization', scope id uuid, parent policy id uuid references patchpolicies(policy id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint patchpolicies org name scope unique unique(definer org id, name, scope type, scope id) ); create table tagpatchpolicyassignments ( assignment id bigserial primary key, tag id uuid not null references tags(tag id) on delete cascade, patch policy id uuid not null references patchpolicies(policy id) on delete cascade, is enabled boolean not null default true, priority integer not null default 0, assigned at timestamptz not null default current timestamp ); create table patchsources ( patch source id serial primary key, name varchar(100) not null unique, e g , 'microsoft', 'apple', 'chocolatey', 'vendorname', 'internal' description text ); comment on table patchsources is 'lookup table for sources/vendors of patches '; \ seed basic sources if desired \ insert into patchsources (name, description) values ('microsoft', 'microsoft security updates (kb articles)'); \ insert into patchsources (name, description) values ('apple', 'apple security updates (apple sa)'); \ insert into patchsources (name, description) values ('chocolatey', 'chocolatey community package repository'); create table patchdefinitions ( patch definition id uuid primary key default gen random uuid(), patch source id integer not null references patchsources(patch source id) on delete restrict, identifier varchar(255) not null, unique identifier within the source (e g , 'kb5001234', 'apple sa 2024 03 15 1', 'googlechrome', 'vendorpatchid xyz') title varchar(512) not null, human readable title/name description text null, severity varchar(20) null check (severity in ('info', 'low', 'medium', 'high', 'critical', 'unknown')), classification varchar(100) null, e g , 'security update', 'critical update', 'feature pack', 'driver', 'application update' release date date null, kb article url text null, link to microsoft kb article security bulletin id varchar(100) null, e g , ms bulletin id vendor url text null, link to vendor advisory/patch page cve ids text\[] null, array of associated cve identifiers (e g , \['cve 2024 1234', 'cve 2024 5678']) cvss base score numeric(3, 1) null, supersedes identifiers text\[] null, optional array of identifiers this patch supersedes is superseded by uuid null references patchdefinitions(patch definition id) on delete set null, optional link to the patch that supersedes this one created at timestamptz not null default current timestamp, updated at timestamptz, auto updated by trigger or sync process constraint patchdefinitions source identifier unique unique (patch source id, identifier) ); comment on table patchdefinitions is 'catalog of known patches/updates from various sources (microsoft, apple, chocolatey, etc ) '; comment on column patchdefinitions identifier is 'unique identifier for the patch within its source (e g , kb number, apple sa id, chocolatey package id) '; comment on column patchdefinitions cve ids is 'array of common vulnerabilities and exposures (cve) ids associated with this patch '; comment on column patchdefinitions is superseded by is 'link to the patch definition id of the patch that replaces this one, if known '; create table patchproductapplicability ( patch applicability id bigserial primary key, patch definition id uuid not null references patchdefinitions(patch definition id) on delete cascade, software product id uuid not null references softwareproducts(software product id) on delete cascade, link to the specific software product/version this patch applies to notes text null, any specific applicability notes unique (patch definition id, software product id) ); comment on table patchproductapplicability is 'maps patches from patchdefinitions to the specific softwareproducts they apply to '; create table monitoringpolicies ( policy id uuid primary key default gen random uuid(), definer org id uuid references organizations(org id) on delete cascade, null for platform defaults, orgid for sp/client policies name varchar(255) not null, name of the monitoring policy/template description text, category varchar(100), for ui grouping (e g , 'windows servers', 'network devices', 'sql monitoring') policy data jsonb not null, defines which monitoringrules are included and potential overrides \ example policy data structure \ { \ "rules" \[ \ { \ "monitoring rule id" "uuid of cpu rule", \ "is enabled" true, \ "override check interval seconds" 300, \ "override check parameters" { "warning threshold" 85, "critical threshold" 95 }, \ "override alert severity critical" "critical", \ "override ticket template id critical" "uuid of critical cpu template" \ }, \ { "monitoring rule id" "uuid of disk rule", "is enabled" true }, \ { "monitoring rule id" "uuid of service rule", "is enabled" false } \ ] \ } is enabled boolean not null default true, is this policy definition active? scope type varchar(20) not null default 'organization' check (scope type in ('global', 'organization', 'location', 'device')), scope where this specific definition/override applies scope id uuid null, links to org, location, or device based on scope type (null if global) parent policy id uuid references monitoringpolicies(policy id) on delete set null, optional link for hierarchical overrides created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint monitoringpolicies org name scope unique unique nulls not distinct (definer org id, name, scope type, scope id) ); create table monitoringrules ( rule id uuid primary key default gen random uuid(), definer org id uuid references organizations(org id) on delete cascade, name varchar(255) not null, description text, category varchar(100), is enabled boolean not null default true, target platform os type text\[], target cpu architectures text\[], check type varchar(30) not null, check parameters jsonb not null, check interval seconds integer not null check (check interval seconds >= 60), execution location varchar(10) not null default 'agent' check (execution location in ('agent', 'probe', 'cloud')), assigned probe device id uuid references devices(device id) on delete set null, warning threshold enabled boolean not null default true, critical threshold enabled boolean not null default true, alert if unavailable boolean not null default false, alert delay seconds integer not null default 0, alert clear delay seconds integer not null default 0, alert severity warning varchar(20) default 'warning' check (alert severity warning in ('info', 'warning', 'critical', 'none')), alert severity critical varchar(20) default 'critical' check (alert severity critical in ('info', 'warning', 'critical', 'none')), notification profile id uuid null references notificationprofiles(profile id) on delete set null, link to detailed notification/escalation profile \ basic actions below might be ignored if notification profile id is set, depending on app logic auto create ticket on varchar(10) not null default 'critical' check (auto create ticket on in ('none', 'warning', 'critical', 'both')), ticket template id warning uuid references tickettemplates(template id) on delete set null, ticket template id critical uuid references tickettemplates(template id) on delete set null, auto resolve ticket boolean not null default true, auto run script on varchar(10) not null default 'none' check (auto run script on in ('none', 'warning', 'critical', 'both')), remediation script id uuid references scripts(script id) on delete set null, remediation delay seconds integer not null default 0, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint monitoringrules org name unique unique nulls not distinct (definer org id, name) ); create table tagmonitoringpolicyassignments ( assignment id bigserial primary key, tag id uuid not null references tags(tag id) on delete cascade, monitoring policy id uuid not null references monitoringpolicies(policy id) on delete cascade, is enabled boolean not null default true, enable/disable this specific tag to policy link priority integer not null default 0, order for resolving conflicts if a device has multiple tags with competing policies (lower number = higher priority) assigned at timestamptz not null default current timestamp \ consider adding a unique constraint on (tag id, monitoring policy id) if needed ); create table sentnotifications ( sent notification id bigserial primary key, org id uuid null references organizations(org id) on delete set null, org context, if applicable triggering event source varchar(50) null, source type e g , 'monitoringrule', 'ticketstatuschange', 'approvalrequest', 'survey', 'manualbroadcast' triggering event id varchar(50) null, id of the source entity (alert id, ticket id, approval request id etc ) notification profile id uuid null references notificationprofiles(profile id) on delete set null, profile used, if applicable escalation step id uuid null references notificationprofileescalationsteps(step id) on delete set null, specific step in the profile, if applicable notification template id uuid null references notificationtemplates(template id) on delete set null, template used, if applicable delivery method varchar(20) not null check (delivery method in ('email', 'sms', 'system', 'webhook', 'push')), how it was sent status varchar(30) not null default 'pending' check (status in ('pending', 'sent', 'delivered', 'failed', 'opened', 'clicked', 'undeliverable', 'queued')), delivery status recipient user id uuid null references users(user id) on delete set null, target commandit user, if applicable recipient contact id uuid null references contacts(contact id) on delete set null, target commandit contact, if applicable recipient group id uuid null references distributiongroups(distribution group id) on delete set null, target group, if applicable recipient address text not null, the actual address/identifier used (email, phone number, user id for system/push) subject text null, snapshot of the subject line (for email) body summary text null, snapshot or summary of the notification body sent at timestamptz null, timestamp when the send attempt was initiated processed at timestamptz null, timestamp when the delivery provider acknowledged/processed it status updated at timestamptz null, timestamp of the last status update (e g , delivered, failed) error message text null, details if the status is 'failed' or 'undeliverable' external message id varchar(255) null, optional id from the external delivery service (e g , sendgrid, twilio) created at timestamptz not null default current timestamp when the notification record was created internally ); comment on table sentnotifications is 'logs individual notifications sent by the system for auditing and status tracking '; comment on column sentnotifications triggering event source is 'indicates what type of event triggered this notification (e g , monitoringrule, ticketstatuschange) '; comment on column sentnotifications triggering event id is 'the id of the specific entity that triggered the notification '; comment on column sentnotifications delivery method is 'the channel used for sending the notification (email, sms, system, etc ) '; comment on column sentnotifications status is 'the current delivery status of the notification '; comment on column sentnotifications recipient address is 'the actual destination address used (email address, phone number, user id) '; comment on column sentnotifications external message id is 'identifier provided by the external delivery service (e g , mailgun, twilio id) '; \ add indexes for common query patterns create index idx sentnotifications status on sentnotifications(status); create index idx sentnotifications recipient user id on sentnotifications(recipient user id); create index idx sentnotifications recipient contact id on sentnotifications(recipient contact id); create index idx sentnotifications recipient address on sentnotifications(recipient address); create index idx sentnotifications triggering event on sentnotifications(triggering event source, triggering event id); create index idx sentnotifications created at on sentnotifications(created at); \ ============================================= \ event log monitoring \ ============================================= \ stores metadata about known os/application events to populate the ui library \ when creating event log monitoring policies/rules defines what agents should look for create table eventlogdefinitions ( definition id uuid primary key default gen random uuid(), owner org id uuid null references organizations(org id) on delete cascade, null for system provided, orgid for msp defined additions os type varchar(20) not null check (os type in ('windows', 'macos', 'linux', 'any')), target os log name varchar(255) not null, e g , 'security', 'system', 'application', 'auth log', 'system log' channel path text null, added specific windows event channel path (e g , 'microsoft windows terminalservices localsessionmanager/operational') source name varchar(255) null, optional specific event source name (e g , 'microsoft windows security auditing', 'sshd') event id varchar(50) not null, event id (integer for windows, potentially string for others) query text null, added optional xml/xpath query for filtering (defaults to if null) level varchar(30) null, typical level (e g , 'information', 'warning', 'error', 'auditsuccess', 'auditfailure'), can be overridden in policy name varchar(255) not null, user friendly name/title for the event description text null, explanation of what this event typically signifies (matches requirement) recommended severity varchar(20) null check (recommended severity in ('info', 'warning', 'critical')), suggested alert severity if monitored mitre attck technique ids text\[] null, optional array of relevant mitre att\&ck technique ids (e g , \['t1078', 't1021 001']) is system defined boolean generated always as (owner org id is null) stored, is this provided by commandit? created at timestamptz not null default current timestamp, updated at timestamptz, unique nulls not distinct (owner org id, os type, name), name unique per os within its scope (system or specific org) unique nulls not distinct (owner org id, os type, log name, channel path, source name, event id) technical uniqueness including channel path ); comment on table eventlogdefinitions is 'stores metadata about known os/application events to define capture rules for agents includes standard logs and specific channel paths '; comment on column eventlogdefinitions owner org id is 'null for definitions provided by commandit, orgid for custom definitions added by an msp '; comment on column eventlogdefinitions log name is 'standard log name (application, security, system) or primary log file name (syslog, auth log) '; comment on column eventlogdefinitions channel path is 'optional, specific windows event log channel path (e g , application, system, security, or deeper like microsoft windows taskscheduler/operational) '; comment on column eventlogdefinitions event id is 'event identifier (numeric for windows, may be string for others) '; comment on column eventlogdefinitions query is 'optional advanced filtering query (e g , xpath for windows event log xml) applied by the agent defaults to match all events matching other criteria if null '; comment on column eventlogdefinitions description is 'explanation of what this event typically signifies '; create index idx eventlogdefinitions owner on eventlogdefinitions(owner org id); create index idx eventlogdefinitions ostype on eventlogdefinitions(os type); create index idx eventlogdefinitions log channel on eventlogdefinitions(log name, channel path); create table eventlogmonitoringpolicies ( policy id uuid primary key default gen random uuid(), definer org id uuid references organizations(org id) on delete cascade, null for platform defaults, orgid for sp/client policies name varchar(255) not null, e g , "default windows security events", "critical linux auth events", "custom app xyz logs" description text, target os types text\[], optional limit policy to \['windows'], \['linux'], \['macos'] policy data jsonb not null, defines events & settings \ example policy data structure \ { \ "event poll frequency seconds" 300, fallback polling interval if subscription fails/not possible \ "windows events" \[ \ { "log name" "security", "event id" 4720, "level" \["auditsuccess"], "keywords include" null, "keywords exclude" null, "is enabled" true, "description" "user account created" }, \ { "log name" "security", "event id" 4740, "level" \["auditfailure"], "is enabled" true, "description" "user account locked out" } \ { "log name" "system", "event id" 7036, "source name" null, "level" \["information"], "keywords include" \["defensive service name"], "is enabled" true, "description" "a defensive service was stopped " } \ ], \ "macos events" \[ \ { "log process" "sshd", "keywords include" \["accepted password for"], "is enabled" true, "description" "ssh connection success" } \ ], \ "linux events" \[ \ { "log facility" "auth", "severity" \["err", "crit"], "keywords include" \["authentication failure"], "is enabled" true, "description" "user authentication failure"} \ ] \ } is enabled boolean not null default true, is this policy definition active? scope type varchar(20) not null default 'organization' check (scope type in ('global', 'organization', 'location', 'device')), scope of application scope id uuid null, parent policy id uuid references eventlogmonitoringpolicies(policy id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz, constraint eventlogpolicies org name scope unique unique nulls not distinct (definer org id, name, scope type, scope id) ); comment on table eventlogmonitoringpolicies is 'defines policies for monitoring specific os event logs '; comment on column eventlogmonitoringpolicies policy data is 'jsonb defining events to capture (by log name, source, id, level, keywords) and settings like privacy masking '; create table tageventlogmonitoringpolicyassignments ( assignment id bigserial primary key, tag id uuid not null references tags(tag id) on delete cascade, event log policy id uuid not null references eventlogmonitoringpolicies(policy id) on delete cascade, is enabled boolean not null default true, priority integer not null default 0, conflict resolution priority assigned at timestamptz not null default current timestamp, unique (tag id, event log policy id) ); comment on table tageventlogmonitoringpolicyassignments is 'applies event log monitoring policies to devices/orgs via tags '; create table notificationprofiles ( profile id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, owning org (msp) name varchar(150) not null, e g , "critical server alerts tiered", "sla breach warning client mgr" description text, is default for org boolean not null default false, is this the default profile for the org? (app logic enforces only one) is active boolean not null default true, can this profile be used? created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (org id, name) ); create table notificationprofiletimerules ( time rule id uuid primary key default gen random uuid(), profile id uuid not null references notificationprofiles(profile id) on delete cascade, name varchar(100) not null, e g , "business hours", "after hours", "weekends", "catch all" priority integer not null default 0, order of evaluation (e g , 0=highest, process first match) business hours id uuid references businesshours(business hours id) on delete set null, optional link to business hours definition \ or define specific days/times if not using businesshours days of week integer\[], optional \[0 6] (sun sat) applies if business hours id is null start time local time null, optional local start time applies if business hours id is null end time local time null, optional local end time applies if business hours id is null is default rule boolean not null default false, catch all if no other time rules match for the profile? created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (profile id, name), unique (profile id, priority), enforce unique priority within a profile check (is default rule = true or business hours id is not null or (days of week is not null and start time local is not null and end time local is not null)) ensure time rule is defined unless default ); create table notificationprofileescalationsteps ( step id uuid primary key default gen random uuid(), time rule id uuid not null references notificationprofiletimerules(time rule id) on delete cascade, escalation level integer not null default 0, 0=initial notification, 1=first escalation, etc delay after previous level seconds integer not null default 0, delay after prior level triggers before this one starts (0 for level 0) repeat interval seconds integer null, how often to re notify at this level if unacknowledged (null = no repeats at this level) max repeats at level integer null, max number of repeat notifications for this specific level (null = repeat indefinitely or until resolved/acked) notify method email boolean not null default true, send standard email notifications? notify method sms boolean not null default false, send sms notifications? (requires consent check) notify method system boolean not null default true, create internal commandit `notifications` record? created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (time rule id, escalation level) only one definition per level within a time rule ); create table notificationprofilesteprecipients ( step recipient id bigserial primary key, step id uuid not null references notificationprofileescalationsteps(step id) on delete cascade, user id uuid null references users(user id) on delete cascade, notify specific user distribution group id uuid null references distributiongroups(distribution group id) on delete cascade, notify a group recipient type varchar(10) generated always as (case when user id is not null then 'user' when distribution group id is not null then 'group' else null end) stored, calculated type added at timestamptz not null default current timestamp, check (user id is not null or distribution group id is not null), must specify a recipient unique (step id, user id) where user id is not null, unique (step id, distribution group id) where distribution group id is not null ); create table executioncontrolpolicies ( policy id uuid primary key default gen random uuid(), org id uuid references organizations(org id) on delete cascade, name varchar(255) not null, description text, is enabled boolean not null default true, scope type varchar(20) not null default 'organization', scope id uuid, parent policy id uuid references executioncontrolpolicies(policy id) on delete set null, default execution action varchar(10) not null default 'deny' check (default execution action in ('allow', 'deny')), default elevation action varchar(20) not null default 'deny' check (default elevation action in ('allow', 'deny', 'standarduser')), created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint execcontrolpolicies org name scope unique unique nulls not distinct (org id, name, scope type, scope id) ); create table executioncontrolrules ( rule id uuid primary key default gen random uuid(), policy id uuid not null references executioncontrolpolicies(policy id) on delete cascade, rule order integer not null default 0, action varchar(20) not null check (action in ('allow', 'deny', 'elevate', 'elevatewithapproval')), match criteria jsonb not null, elevation justification required boolean default false, elevation approval workflow id uuid references approvalworkflowdefinitions(definition id) on delete set null, description text, is enabled boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table tagexecutioncontrolpolicyassignments ( assignment id bigserial primary key, tag id uuid not null references tags(tag id) on delete cascade, execution control policy id uuid not null references executioncontrolpolicies(policy id) on delete cascade, is enabled boolean not null default true, priority integer not null default 0, assigned at timestamptz not null default current timestamp ); create table storagecontrolpolicies ( policy id uuid primary key default gen random uuid(), org id uuid references organizations(org id) on delete cascade, name varchar(255) not null, description text, is enabled boolean not null default true, scope type varchar(20) not null default 'organization', scope id uuid, parent policy id uuid references storagecontrolpolicies(policy id) on delete set null, default action varchar(10) not null default 'deny' check (default action in ('allow', 'deny', 'readonly')), created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint storagecontrolpolicies org name scope unique unique nulls not distinct (org id, name, scope type, scope id) ); create table storagecontrolrules ( rule id uuid primary key default gen random uuid(), policy id uuid not null references storagecontrolpolicies(policy id) on delete cascade, rule order integer not null default 0, action varchar(10) not null check (action in ('allow', 'deny', 'readonly')), match criteria jsonb not null, description text, is enabled boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table tagstoragecontrolpolicyassignments ( assignment id bigserial primary key, tag id uuid not null references tags(tag id) on delete cascade, storage control policy id uuid not null references storagecontrolpolicies(policy id) on delete cascade, is enabled boolean not null default true, priority integer not null default 0, assigned at timestamptz not null default current timestamp ); create table policyattestationassignments ( assignment id uuid primary key default gen random uuid(), policy document id uuid not null references documents(document id) on delete restrict, required revision id uuid references documentrevisions(revision id) on delete set null, assigned to user id uuid references users(user id) on delete cascade, assigned to role id uuid references roles(role id) on delete cascade, assigned to org id uuid references organizations(org id) on delete cascade, assigned to tag id uuid references tags(tag id) on delete cascade, assigning user id uuid references users(user id) on delete set null, assigned at timestamptz not null default current timestamp, due date date, is recurring boolean not null default false, recurrence interval interval, last recurrence at date, is active boolean not null default true, check (assigned to user id is not null or assigned to role id is not null or assigned to org id is not null or assigned to tag id is not null) ); create table userpolicyattestations ( attestation id bigserial primary key, assignment id uuid not null references policyattestationassignments(assignment id) on delete cascade, user id uuid not null references users(user id) on delete cascade, attested document id uuid not null references documents(document id) on delete restrict, attested revision id uuid not null references documentrevisions(revision id) on delete restrict, attested at timestamptz not null default current timestamp, ip address inet, user agent text, unique (assignment id, user id) ); \ ============================================= \ it service management (itsm) \ ============================================= create table notificationtemplates ( template id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, defining org (msp) name varchar(255) not null, unique name for the template within org description text, notification type varchar(20) not null check (notification type in ('email', 'system', 'sms')), usage context varchar(50), optional e g , 'ticketstatuschange', 'approvalrequest' subject template text null, subject line (primarily for email type), supports variables body template text not null, template body (html, markdown, or plain text), supports variables body content type varchar(10) not null default 'html' check (body content type in ('html', 'markdown', 'text')), is system boolean not null default false, is this a non editable system default? is active boolean not null default true, can this template be selected/used? created at timestamptz not null default current timestamp, updated at timestamptz, auto updated by trigger constraint notificationtemplates org name unique unique(org id, name) ); create table alerts ( alert id bigserial primary key, referenced as bigint by tickets org id uuid not null references organizations(org id) on delete cascade, client organization experiencing the alert device id uuid null references devices(device id) on delete set null, source device, if applicable monitoring rule id uuid null references monitoringrules(rule id) on delete set null, link to the rule that triggered this compliance rule id uuid null references compliancerules(rule id) on delete set null, link if triggered by compliance alert source varchar(100), name/identifier of the source (e g , monitoring rule name, 'emailtriage', 'edr integration') alert signature text null, unique signature identifying the specific alert condition (e g , device+check+severity) used for state tracking alert timestamp timestamptz not null default current timestamp, when the condition was initially detected/alert record created severity varchar(20) not null check (severity in ('info', 'warning', 'critical', 'ok')), severity when created/updated title varchar(512) not null, short summary of the alert condition details jsonb, structured details, metrics, event data, etc threat details jsonb, specific fields for edr/security threats status varchar(25) not null default 'new' check (status in ( 'new', initial state, thresholds potentially not met 'acknowledged', human acknowledged, investigation may be ongoing 'ticketcreated', ticket automatically created by rule 'actionattempted', automated action (script, etc ) initiated 'actionfailed', automated action failed 'actionsucceeded', automated action completed successfully 'suppressed maint', alert occurred but was suppressed due to maintenance window 'resolved', clearing condition met or manually resolved 'closed', final state after resolution/acknowledgement 'unknown' error state or indeterminate )), first occurred at timestamptz not null default current timestamp, when this signature first triggered this active alert instance last occurred at timestamptz not null default current timestamp, timestamp condition last observed for this active instance occurrence count integer not null default 1, how many times re triggered while active action taken flag boolean not null default false, set true once a threshold action (createticket, runscript, etc ) executed for this instance related ticket id bigint null references tickets(ticket id) on delete set null, link if a ticket was created/linked acknowledged by user id uuid null references users(user id) on delete set null, acknowledged at timestamptz null, resolved by user id uuid null references users(user id) on delete set null, user who manually resolved/cleared resolved at timestamptz null, when alert transitioned to resolved state closed by user id uuid null references users(user id) on delete set null, closed at timestamptz null, resolution notes text optional notes on how it was resolved or why closed/suppressed ); \ index for finding active alerts by signature create index idx alerts signature status on alerts(alert signature, status); create index idx alerts last occurred on alerts(last occurred at); useful for cleanup/reporting create index idx alerts org status on alerts(org id, status); comment on column alerts alert signature is 'unique signature identifying the specific alert condition (e g , device+check+severity) used for state tracking and thresholds '; comment on column alerts status is 'current lifecycle state of the alert instance '; comment on column alerts first occurred at is 'timestamp when this specific alert signature first became active (entered ''new'' state) '; comment on column alerts last occurred at is 'timestamp when this specific alert signature was last seen while in an active state '; comment on column alerts occurrence count is 'number of times this specific alert signature was received while the alert instance was active '; comment on column alerts action taken flag is 'set to true once a threshold based action (like createticket or runscript) has been executed for this active alert instance to prevent duplicates '; create table alertactionslog ( alert action log id bigserial primary key, alert id bigint not null references alerts(alert id) on delete cascade, link to the triggering alert action type varchar(20) not null check (action type in ('createticket', 'runscript')), type of automated action attempted monitoring rule id uuid null references monitoringrules(rule id) on delete set null, denormalized rule that defined the action status varchar(20) not null check (status in ('attempted', 'success', 'failure')), outcome of the action execution execution timestamp timestamptz not null default current timestamp, when the action was executed/attempted related ticket id bigint null references tickets(ticket id) on delete set null, link to the ticket created (if action type='createticket') related command queue id bigint null references agentcommandqueue(command queue id) on delete set null, link to the command sent (if action type='runscript') error message text null details if status is 'failure' ); comment on table alertactionslog is 'logs the execution and outcome of automated actions (create ticket, run script) triggered by alerts based on monitoring rule definitions '; create table businesshours ( business hours id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(100) not null, type varchar(20) not null check (type in ('fixedtimezone', 'locationlocaltime')), fixed time zone varchar(100), created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint businesshours org name unique unique(org id, name) ); create table businesshoursslots ( slot id bigserial primary key, business hours id uuid not null references businesshours(business hours id) on delete cascade, day of week integer not null check (day of week >= 0 and day of week <= 6), start time local time not null, end time local time not null, check (end time local > start time local) ); create table serviceboards ( board id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(255) not null, description text, default assigned user id uuid references users(user id) on delete set null, default ticket template id uuid references tickettemplates(template id) on delete set null, enable ai triage boolean not null default false, inbound email address varchar(255) unique, is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint serviceboards org name unique unique(org id, name) ); create table ticketstatuses ( status id uuid primary key default gen random uuid(), board id uuid not null references serviceboards(board id) on delete cascade, internal name varchar(100) not null, external name varchar(100) not null, is resolved flag boolean not null default false, is closed flag boolean not null default false, is cancelled flag boolean not null default false, pauses sla clock boolean not null default false, does this status pause sla timers? allow time entry boolean not null default true, is default status boolean not null default false, sort order integer not null default 0, is active boolean not null default true, email notify on entry boolean not null default false, email notify template id uuid references notificationtemplates(template id) on delete set null, internal notify on entry boolean not null default false, internal notify template id uuid references notificationtemplates(template id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint ticketstatuses board name unique unique(board id, internal name) ); create table ticketcategories ( category id uuid primary key default gen random uuid(), board id uuid not null references serviceboards(board id) on delete cascade, name varchar(100) not null, description text, is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint ticketcategories board name unique unique(board id, name) ); create table tickettypes ( type id uuid primary key default gen random uuid(), board id uuid not null references serviceboards(board id) on delete cascade, category id uuid references ticketcategories(category id) on delete set null, name varchar(100) not null, ticket nature varchar(20) not null default 'incident' check (ticket nature in ('incident', 'servicerequest', 'change', 'problem', 'other')), description text, is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint tickettypes board name unique unique(board id, name) ); create table ticketsubtypes ( sub type id uuid primary key default gen random uuid(), type id uuid not null references tickettypes(type id) on delete cascade, name varchar(100) not null, description text, is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint ticketsubtypes type name unique unique(type id, name) ); create table ticketitems ( item id uuid primary key default gen random uuid(), sub type id uuid not null references ticketsubtypes(sub type id) on delete cascade, name varchar(100) not null, description text, is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint ticketitems subtype name unique unique(sub type id, name) ); create table ticketimpact ( impact id serial primary key, name varchar(50) not null unique, description text ); create table ticketurgency ( urgency id serial primary key, name varchar(50) not null unique, description text ); create table ticketpriority ( priority id serial primary key, name varchar(50) not null unique, description text, is default boolean not null default false, sort order integer not null default 0 ); create table ticketprioritymatrix ( impact id integer not null references ticketimpact(impact id) on delete cascade, urgency id integer not null references ticketurgency(urgency id) on delete cascade, priority id integer not null references ticketpriority(priority id) on delete restrict, primary key (impact id, urgency id) ); create table tickettemplates ( template id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(255) not null, description text, scope type varchar(20) not null default 'organization', scope id uuid, parent template id uuid references tickettemplates(template id) on delete set null, is enabled boolean not null default true, available to varchar(10) not null default 'techonly' check (available to in ('techonly', 'useronly', 'all')), required role ids jsonb, target board id uuid not null references serviceboards(board id) on delete restrict, default ticket data jsonb not null, form questions jsonb, for initial data gathering default tasks data jsonb, default required equipment type ids uuid\[], default required certification ids uuid\[], initiation chatbot flow id uuid references chatbotflows(flow id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint tickettemplates org name scope unique unique nulls not distinct (org id, name, scope type, scope id) ); create table skills ( skill id uuid primary key default gen random uuid(), name varchar(100) not null unique, description text ); create table userskills ( user skill id bigserial primary key, user id uuid not null references users(user id) on delete cascade, skill id uuid not null references skills(skill id) on delete cascade, changed to fk skill level integer check (skill level >= 1 and skill level <= 5), unique (user id, skill id) kept unique constraint ); create table tickets ( ticket id bigserial primary key, org id uuid not null references organizations(org id) on delete cascade, ticket number varchar(20) not null unique, requester user id uuid references users(user id) on delete set null, requester email varchar(255), callback phone number varchar(50), subject varchar(255) not null, description text, board id uuid not null references serviceboards(board id) on delete restrict, status id uuid not null references ticketstatuses(status id) on delete restrict, priority id integer references ticketpriority(priority id) on delete set null, impact id integer references ticketimpact(impact id) on delete set null, urgency id integer references ticketurgency(urgency id) on delete set null, category id uuid references ticketcategories(category id) on delete set null, type id uuid not null references tickettypes(type id) on delete restrict, sub type id uuid references ticketsubtypes(sub type id) on delete set null, item id uuid references ticketitems(item id) on delete set null, assigned to user id uuid references users(user id) on delete set null, primary device id uuid references devices(device id) on delete set null, site location id uuid references locations(location id) on delete set null, source varchar(50) default 'agent', budget hours numeric(10,2), actual hours numeric(10,2) default 0 00, has pending field request boolean default false, due date timestamptz, optional manual overall due date calculated response due utc timestamptz null, sla engine calculated response deadline calculated resolution due utc timestamptz null, sla engine calculated resolution deadline created at timestamptz not null default current timestamp, updated at timestamptz, auto updated first response at timestamptz, resolved at timestamptz, closed at timestamptz, sla timer paused boolean not null default false, sla paused timestamp timestamptz, response sla status varchar(20) default 'na' check (response sla status in ('met', 'breached', 'atrisk', 'paused', 'na')), resolution sla status varchar(20) default 'na' check (resolution sla status in ('met', 'breached', 'atrisk', 'paused', 'na')), change request id uuid references changerequests(change request id) on delete set null, problem id uuid references problems(problem id) on delete set null, service catalog item id uuid references servicecatalogitems(service catalog item id) on delete set null, originating alert id bigint references alerts(alert id) on delete set null, originating sales order id uuid references salesorders(portal order id) on delete set null, related project id uuid references projects(project id) on delete set null, scheduled start time utc timestamptz, scheduled end time utc timestamptz, is major incident boolean not null default false, custom fields jsonb, ai summary text, form answers jsonb ); create table relatedtickets ( ticket id a bigint not null references tickets(ticket id) on delete cascade, ticket id b bigint not null references tickets(ticket id) on delete cascade, relationship type varchar(20) not null check (relationship type in ('duplicate', 'related', 'causedby', 'parentof', 'childof')), linked at timestamptz not null default current timestamp, linked by user id uuid references users(user id) on delete set null, primary key (ticket id a, ticket id b), check (ticket id a < ticket id b) ensure canonical ordering to prevent duplicate pairs ); create table ticketstatushistory ( status history id bigserial primary key, ticket id bigint not null references tickets(ticket id) on delete cascade, old status id uuid references ticketstatuses(status id) on delete set null, new status id uuid not null references ticketstatuses(status id) on delete restrict, changed at timestamptz not null default current timestamp, changed by user id uuid references users(user id) on delete set null ); create table ticketupdates ( update id bigserial primary key, ticket id bigint not null references tickets(ticket id) on delete cascade, user id uuid references users(user id) on delete set null, user or ai creating the update timestamp timestamptz not null default current timestamp, when the update occurred body text not null, is internal note boolean not null default false, note type varchar(30) null check (note type in ('standard', 'internal', 'emaillog', 'systemevent', 'aisummary')), categorizes the update created at timestamptz not null default current timestamp when the record was created \ updated at can be added if edits are allowed ); comment on table ticketupdates is 'stores notes, replies, and logs associated with a ticket '; comment on column ticketupdates note type is 'categorizes the type of update (standard user note, internal tech note, log of processed email, automated system event, ai generated summary, etc ) helps distinguish ai logged emails '; create index idx ticketupdates note type on ticketupdates(note type); create table ticketupdateattachments ( ticket update attachment id bigserial primary key, ticket update id bigint not null references ticketupdates(update id) on delete cascade, link to the specific note/update attachment id uuid not null references attachments(attachment id) on delete cascade, link to the file attachment added at timestamptz not null default current timestamp, unique (ticket update id, attachment id) prevent linking same file twice to same note ); comment on table ticketupdateattachments is 'links file attachments to specific ticketupdates (e g , attachments from a chat transcript saved as a note) '; create table tickettasks ( ticket task id bigserial primary key, ticket id bigint not null references tickets(ticket id) on delete cascade, template task id varchar(100), optional identifier linking back to task definition in tickettemplate sequence order integer not null default 0, display/execution order within the ticket depends on ticket task id bigint null references tickettasks(ticket task id) on delete set null, prerequisite task within the same ticket description text not null, status varchar(20) not null default 'pending' check (status in ('pending', 'inprogress', 'awaitingapproval', 'complete', 'skipped', 'failed')), requires approval boolean not null default false, does this task trigger an approval? approval request id uuid references approvalrequests(approval request id) on delete set null, link to the triggered approvalrequest assigned user id uuid references users(user id) on delete set null, specific user assigned (if manual) due date timestamptz, is checklist item boolean not null default false, simple check off vs substantive work? execution type varchar(20) not null default 'manual' check (execution type in ('manual', 'automation')), automation script id uuid references scripts(script id) on delete set null, link if execution type='automation' (simple script) automation workflow id uuid references automations(automation id) on delete set null, link if execution type='automation' (complex workflow) automation trigger varchar(50), condition triggering automation ('onticketcreation', 'ontaskstatuschange', 'manual') input variables jsonb, variables passed to automation script/workflow requirements definition jsonb, prerequisites for automated tasks estimated duration seconds integer, estimated tech effort completion notes text, completed at timestamptz, created by ai boolean not null default false, was this task suggested/created by ai? created at timestamptz not null default current timestamp, updated at timestamptz auto updated by trigger ); create table ticketaffectedcis ( ticket id bigint not null references tickets(ticket id) on delete cascade, ci type varchar(50) not null, ci id varchar(36) not null, relationship notes text, primary key (ticket id, ci type, ci id) ); create table ticketrequiredskills ( ticket id bigint not null references tickets(ticket id) on delete cascade, skill id uuid not null references skills(skill id) on delete cascade, primary key (ticket id, skill id) ); create table ticketproductsused ( ticket product id bigserial primary key, ticket id bigint not null references tickets(ticket id) on delete cascade, product id uuid not null references products(product id) on delete restrict, quantity numeric(19,4) not null, serial number varchar(255), if product is serialized unit price numeric(19,4), price used for this instance (may override product default) is billable boolean default true, inventory transaction id bigint references inventorytransactions(transaction id) on delete set null, link to stock deduction added at timestamptz not null default current timestamp, added by user id uuid references users(user id) on delete set null ); create table fieldservicerequests ( field service request id uuid primary key default gen random uuid(), ticket id bigint not null references tickets(ticket id) on delete restrict, requesting user id uuid references users(user id) on delete set null, request timestamp timestamptz not null default current timestamp, reason text, requested start datetime timestamptz, requested end datetime timestamptz, requested time preference varchar(50), estimated duration seconds integer, urgency level integer, notes for dispatcher text, intended assignee type varchar(20) default 'internaluser' check (intended assignee type in ('internaluser', 'externalvendor')), intended vendor org id uuid null references organizations(org id) on delete set null, status varchar(20) default 'pending' check (status in ('pending', 'scheduled', 'denied', 'cancelled', 'pendingreview')), dispatcher user id uuid references users(user id) on delete set null, handling timestamp timestamptz, resulting schedule entry id bigint references technicianschedules(schedule entry id) on delete set null, denial reason text ); create table fieldservicerequestrequiredequipment ( field service request id uuid not null references fieldservicerequests(field service request id) on delete cascade, equipment type id integer not null references equipmenttypes(equipment type id) on delete cascade, notes text, e g , "need 8ft ladder" primary key (field service request id, equipment type id) ); create table locationrequiredequipment ( location id uuid not null references locations(location id) on delete cascade, equipment type id integer not null references equipmenttypes(equipment type id) on delete cascade, notes text, primary key (location id, equipment type id) ); create table fieldservicerequestrequiredcertifications ( field service request id uuid not null references fieldservicerequests(field service request id) on delete cascade, certification id uuid not null references certifications(certification id) on delete cascade, primary key (field service request id, certification id) ); create table travelbillingpolicies ( travel billing policy id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, msp defining the policy name varchar(150) not null, e g , "standard client travel", "project onsite travel (no first/last)" is default for org boolean not null default false, only one default policy per org allowed billing method varchar(20) not null check (billing method in ('time', 'mileage', 'flatrate', 'timeormileagegreater', 'timeandmileage', 'none')), travel work type id uuid references worktypes(work type id) on delete set null, work type for billing travel time mileage product id uuid references products(product id) on delete set null, product for billing mileage flat rate product id uuid references products(product id) on delete set null, product for billing flat rate flat rate amount override numeric(19,4), optional override if flat rate product id is null first trip rule varchar(25) default 'billable' check (first trip rule in ('billable', 'nonbillable', 'billableafterthreshold', 'differentrate')), first trip threshold km integer, first trip threshold minutes integer, first trip rate modifier varchar(50), last trip rule varchar(25) default 'billable' check (last trip rule in ('billable', 'nonbillable', 'billableafterthreshold', 'differentrate')), last trip threshold km integer, last trip threshold minutes integer, last trip rate modifier varchar(50), inter site travel rule varchar(25) default 'billtodestination' check (inter site travel rule in ('billtodestination', 'billtoorigin', 'splitequally', 'nonbillable')), distance unit varchar(2) default 'km' check (distance unit in ('km', 'mi')), notes text, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (org id, name) ); create table problems ( problem id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, client org experiencing the problem problem number varchar(20) not null unique, human readable sequence title varchar(255) not null, summary of the underlying problem description text, detailed description of symptoms, scope, impact, history status varchar(50) not null default 'open' check (status in ('open', 'investigating', 'rootcauseidentified', 'workaroundavailable', 'solutionpendingchange', 'resolved', 'closed')), priority id integer null references ticketpriority(priority id) on delete set null, problem priority urgency id integer null references ticketurgency(urgency id) on delete set null, problem urgency impact id integer null references ticketimpact(impact id) on delete set null, problem impact reported date timestamptz not null default current timestamp, when problem record created assigned to user id uuid references users(user id) on delete set null, problem manager/investigator root cause summary text, summary of the identified root cause workaround details text, description of any temporary workaround permanent solution details text, description of the permanent solution related change request id uuid references changerequests(change request id) on delete set null, link to rfc implementing solution inbound email address varchar(255) unique null; created at timestamptz not null default current timestamp, updated at timestamptz, auto updated resolved at timestamptz, when the permanent solution was confirmed implemented closed at timestamptz when the problem record was formally closed ); create table problemincidents ( m2m linking problems to related incident tickets problem id uuid not null references problems(problem id) on delete cascade, ticket id bigint not null references tickets(ticket id) on delete cascade, linked at timestamptz not null default current timestamp, linked by user id uuid references users(user id) on delete set null, primary key (problem id, ticket id) ); create table problemupdates ( problem update id bigserial primary key, problem id uuid not null references problems(problem id) on delete cascade, user id uuid references users(user id) on delete set null, user or ai creating the update timestamp timestamptz not null default current timestamp, body text not null, content of the note or processed email body note type varchar(30) null check (note type in ('standard', 'internal', 'emaillog', 'systemevent')), categorizes the update created at timestamptz not null default current timestamp ); create index idx problemupdates problem id on problemupdates(problem id, timestamp desc); comment on table problemupdates is 'stores notes, logs of emails received via problem email address, and other updates related to a problem record '; create table problemupdateattachments ( problem update attachment id bigserial primary key, problem update id bigint not null references problemupdates(problem update id) on delete cascade, attachment id uuid not null references attachments(attachment id) on delete cascade, added at timestamptz not null default current timestamp, unique (problem update id, attachment id) ); comment on table problemupdateattachments is 'links file attachments (stored in attachments table) to specific problemupdates '; create table changerequests ( change request id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, request number varchar(20) not null unique, title varchar(255) not null, description text, detailed description of the change justification text, reason for the change implementation plan text null, steps to implement test plan text null, how success will be tested backout plan text, steps to revert if needed requester user id uuid not null references users(user id) on delete restrict, change type varchar(20) not null default 'normal' check (change type in ('standard', 'normal', 'emergency')), priority id integer null references ticketpriority(priority id) on delete set null, risk level varchar(20) check (risk level in ('low', 'medium', 'high', 'critical')), impact level varchar(20) check (impact level in ('low', 'medium', 'high', 'critical')), status varchar(20) not null default 'pending' check (status in ('pending', 'assessing', 'pendingapproval', 'approved', 'rejected', 'scheduled', 'implementing', 'completed', 'failed', 'cancelled')), approval request id uuid references approvalrequests(approval request id) on delete set null, project id uuid null references projects(project id) on delete set null, scheduled start date timestamptz, scheduled end date timestamptz, actual start date timestamptz, actual end date timestamptz, inbound email address varchar(255) unique null; review notes text, for post implementation review (pir) created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); comment on column changerequests inbound email address is 'unique email address ({guid}@commandit net) for sending updates directly to this change request record '; create table changerequestupdates ( change update id bigserial primary key, change request id uuid not null references changerequests(change request id) on delete cascade, user id uuid references users(user id) on delete set null, user or ai creating the update timestamp timestamptz not null default current timestamp, body text not null, content of the note or processed email body note type varchar(30) null check (note type in ('standard', 'internal', 'emaillog', 'systemevent')), categorizes the update created at timestamptz not null default current timestamp ); create index idx changeupdates change id on changerequestupdates(change request id, timestamp desc); comment on table changerequestupdates is 'stores notes, logs of emails received via change request email address, and other updates related to a change request record '; create table changerequestupdateattachments ( change update attachment id bigserial primary key, change update id bigint not null references changerequestupdates(change update id) on delete cascade, attachment id uuid not null references attachments(attachment id) on delete cascade, added at timestamptz not null default current timestamp, unique (change update id, attachment id) ); comment on table changerequestupdateattachments is 'links file attachments (stored in attachments table) to specific changerequestupdates '; create table blackoutperiods ( blackout period id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, org defining the blackout name varchar(150) not null, e g , "end of month freeze", "holiday change freeze" reason text, start time utc timestamptz not null, end time utc timestamptz not null, affects all cis boolean not null default true, does it apply globally or to specific items? is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated check (end time utc > start time utc) \ consider m2m tables to link blackoutperiods to specific orgs, locations, device types, or cis if affects all cis is false ); create table changerequestaffectedcis ( affected ci id bigserial primary key, change request id uuid not null references changerequests(change request id) on delete cascade, ci type varchar(50) not null, ci id varchar(36) not null, relationship notes text, unique(change request id, ci type, ci id) ); create table recurringticketschedules ( schedule id uuid primary key default gen random uuid(), definer org id uuid not null references organizations(org id) on delete cascade, name varchar(255) not null, description text, is active boolean not null default true, ticket template id uuid not null references tickettemplates(template id) on delete restrict, template variable values jsonb, target client org id uuid not null references organizations(org id) on delete cascade, schedule type varchar(20) not null check (schedule type in ('monthly', 'weekly', 'daily')), interval integer not null default 1 check (interval > 0), days of week integer\[], monthly occurrence type varchar(30), monthly day of month integer, monthly ordinal integer, monthly day specifier varchar(20), scheduled time utc time not null, next run at utc timestamptz, last run at utc timestamptz, last generated ticket id bigint references tickets(ticket id) on delete set null, error message text, scope type varchar(20) not null default 'organization', scope id uuid, parent schedule id uuid references recurringticketschedules(schedule id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated check ( / ensure valid schedule combinations / ), unique(definer org id, name, scope type, scope id) ); create table recurringscheduledevicetargets ( schedule id uuid not null references recurringticketschedules(schedule id) on delete cascade, device id uuid not null references devices(device id) on delete cascade, assigned at timestamptz not null default current timestamp, primary key (schedule id, device id) ); create table recurringscheduletagtargets ( schedule id uuid not null references recurringticketschedules(schedule id) on delete cascade, tag id uuid not null references tags(tag id) on delete cascade, assigned at timestamptz not null default current timestamp, primary key (schedule id, tag id) ); create table servicecatalogitems ( service catalog item id uuid primary key default gen random uuid(), org id uuid references organizations(org id) on delete cascade, name varchar(255) not null, description text, category varchar(100), icon url text, is active boolean not null default true, display price numeric(19,4), estimated delivery time varchar(50), fulfillment ticket template id uuid not null references tickettemplates(template id) on delete restrict, requires approval boolean not null default false, approval workflow definition id uuid references approvalworkflowdefinitions(definition id) on delete set null, initiation chatbot flow id uuid references chatbotflows(flow id) on delete set null, available to all orgs boolean not null default true, available to org ids uuid\[], requires specific roles boolean not null default false, available to role ids uuid\[], created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint servicecatalogitems org name unique unique nulls not distinct (org id, name) ); create table servicecatalogitemkbdocs ( service catalog item id uuid not null references servicecatalogitems(service catalog item id) on delete cascade, document id uuid not null references documents(document id) on delete cascade, relationship type varchar(50) default 'relatedinfo', primary key (service catalog item id, document id) ); create table deviceintake ( intake id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, location id uuid not null references locations(location id) on delete restrict, ticket id bigint not null references tickets(ticket id) on delete restrict, device id uuid references devices(device id) on delete set null, received item description text not null, customer reported issue text, arrival method varchar(20) not null check (arrival method in ('mailincourier', 'customerdropoff', 'fielddropoff', 'newstock', 'other')), received at timestamptz not null default current timestamp, received by user id uuid references users(user id) on delete set null, inbound courier name varchar(100), inbound tracking number varchar(100), sender details jsonb, dropped off by name varchar(255), dropped off by user id uuid references users(user id) on delete set null, status varchar(30) not null default 'received' check (status in ('received', 'intakecomplete', 'onbench', 'awaitingparts', 'readyforreturn', 'returned', 'disposed')), notes text, return method varchar(20) check (return method in ('mailoutcourier', 'customerpickup', 'fieldpickup', 'disposed', 'other')), returned at timestamptz, returned by user id uuid references users(user id) on delete set null, outbound courier name varchar(100), outbound tracking number varchar(100), shipping address details jsonb, picked up by name varchar(255), picked up by user id uuid references users(user id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table workbenches ( bench id uuid primary key default gen random uuid(), location id uuid not null references locations(location id) on delete cascade, name varchar(100) not null, description text, is active boolean not null default true, specialized skills text\[], match technicianskills? or equipmenttypes? prefer equipmenttypes created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique(location id, name) ); create table benchassignments ( bench assignment id bigserial primary key, bench id uuid not null references workbenches(bench id) on delete restrict, ticket id bigint not null references tickets(ticket id) on delete cascade, device id uuid references devices(device id) on delete set null, originating intake id uuid references deviceintake(intake id) on delete set null, scheduled start time timestamptz not null, scheduled end time timestamptz not null, estimated bench duration seconds integer check (estimated bench duration seconds >= 0), actual start time timestamptz, actual end time timestamptz, status varchar(30) not null default 'scheduled' check (status in ('scheduled', 'inprogress', 'onholdawaitingparts', 'completed', 'cancelled')), assigned technician user id uuid references users(user id) on delete set null, notes text, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated check (scheduled end time > scheduled start time) \ constraint prevent bench overlaps exclude using gist ( ) requires gist index setup ); create table distributiongroups ( distribution group id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(100) not null, description text, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (org id, name) ); create table distributiongroupmembers ( distribution group member id bigserial primary key, distribution group id uuid not null references distributiongroups(distribution group id) on delete cascade, user id uuid references users(user id) on delete cascade, contact id uuid references contacts(contact id) on delete cascade, email address varchar(255), added at timestamptz not null default current timestamp, check (user id is not null or contact id is not null or email address is not null), unique (distribution group id, user id) where user id is not null, unique (distribution group id, contact id) where contact id is not null, unique (distribution group id, email address) where email address is not null ); create table ticketfollowers ( ticket id bigint not null references tickets(ticket id) on delete cascade, user id uuid not null references users(user id) on delete cascade, followed at timestamptz not null default current timestamp, notification level varchar(20) default 'allupdates', primary key (ticket id, user id) ); create table fieldservicerequestrequiredequipment ( field service request id uuid not null references fieldservicerequests(field service request id) on delete cascade, equipment type id integer not null references equipmenttypes(equipment type id) on delete cascade, notes text, specific notes, e g , "need 8ft ladder" primary key (field service request id, equipment type id) ); create table certifications ( new table for certifications certification id uuid primary key default gen random uuid(), name varchar(150) not null unique, description text, issuing body varchar(100), validity period months integer null if no expiry ); create table usercertifications ( new m2m table user certification id bigserial primary key, user id uuid not null references users(user id) on delete cascade, certification id uuid not null references certifications(certification id) on delete cascade, issue date date, expiry date date, automatically calculated or entered certification number varchar(100), verified at timestamptz, verified by user id uuid references users(user id) on delete set null, unique (user id, certification id) ); create table fieldservicerequestrequiredcertifications ( field service request id uuid not null references fieldservicerequests(field service request id) on delete cascade, certification id uuid not null references certifications(certification id) on delete cascade, primary key (field service request id, certification id) ); create table locationrequiredcertifications ( location id uuid not null references locations(location id) on delete cascade, certification id uuid not null references certifications(certification id) on delete cascade, primary key (location id, certification id) ); create table formtemplates ( form template id uuid primary key default gen random uuid(), org id uuid references organizations(org id) on delete cascade, defining org (null for system) name varchar(255) not null, description text, structure definition jsonb not null, json defining form fields, types, options, validation, potentially mapping to controls usage context text\[], optional tags indicating where form is typically used (e g , \['sitesurvey', 'hipaa assessment', 'onboardingchecklist']) is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique nulls not distinct (org id, name) ); create table forminstances ( form instance id uuid primary key default gen random uuid(), form template id uuid not null references formtemplates(form template id) on delete restrict, ticket id bigint references tickets(ticket id) on delete set null, link to ticket schedule entry id bigint references technicianschedules(schedule entry id) on delete set null, link to schedule entry org id uuid references organizations(org id) on delete set null, context org (e g , client for assessment) location id uuid references locations(location id) on delete set null, context location device id uuid references devices(device id) on delete set null, context device technician user id uuid references users(user id) on delete set null, user who filled it out submission timestamp timestamptz null, when form was marked as submitted/completed status varchar(20) default 'pending' check (status in ('pending', 'inprogress', 'completed', 'requiresreview')), form data jsonb, json storing the actual filled values { "field id" "value", } created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); \ ============================================= \ inventory & procurement \ ============================================= \ products defined in billing section create table warehouses ( warehouse id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, owning org (msp) name varchar(100) not null, e g , 'main warehouse', 'tech van john doe' location id uuid references locations(location id) on delete set null, physical location if applicable warehouse type varchar(20) not null check (warehouse type in ('main', 'van', 'sitecache', 'virtual')), technician user id uuid references users(user id) on delete set null, link if van type is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (org id, name), unique (technician user id) where warehouse type = 'van' and technician user id is not null only one van per tech ); create table inventorystocklevels ( stock level id bigserial primary key, product id uuid not null references products(product id) on delete cascade, warehouse id uuid not null references warehouses(warehouse id) on delete cascade, changed from location id quantity on hand numeric(19,4) not null default 0 00, quantity allocated numeric(19,4) not null default 0 00, quantity on order numeric(19,4) not null default 0 00, reorder point numeric(19,4), minimum level numeric(19,4), maximum level numeric(19,4), last updated at timestamptz not null default current timestamp, unique (product id, warehouse id) ); create table inventorytransactions ( transaction id bigserial primary key, product id uuid not null references products(product id) on delete restrict, warehouse id uuid not null references warehouses(warehouse id) on delete restrict, source/dest warehouse ticket id bigint references tickets(ticket id) on delete set null, optional link to ticket where used schedule entry id bigint references technicianschedules(schedule entry id) on delete set null, optional link to schedule entry technician user id uuid references users(user id) on delete set null, user performing transaction quantity changed numeric(19,4) not null, negative for usage/out, positive for receive/in serial number varchar(255), if serialized item transaction type varchar(20) not null check (transaction type in ('usedonticket', 'received', 'transferout', 'transferin', 'adjustup', 'adjustdown', 'initialstock')), related transaction id bigint references inventorytransactions(transaction id) on delete set null, link for transfers (e g , transferout links to transferin) destination warehouse id uuid null references warehouses(warehouse id) on delete restrict, for transferout/in timestamp timestamptz not null default current timestamp, notes text ); create table itemreceipts ( receipt id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, location id uuid not null references locations(location id) on delete restrict, receipt number varchar(50) not null unique, receipt date date not null default current date, purchase order id uuid references purchaseorders(purchase order id) on delete set null, vendor org id uuid references organizations(org id) on delete set null, packing slip number varchar(100), notes text, received by user id uuid references users(user id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table itemreceiptlines ( receipt line id bigserial primary key, receipt id uuid not null references itemreceipts(receipt id) on delete cascade, po line item id bigint references purchaseorderlineitems(po line item id) on delete set null, product id uuid references products(product id) on delete restrict, description text not null, quantity received numeric(19,4) not null, unit cost numeric(19,4) null, actual cost of received item (nullable) currency code varchar(3) null, currency of the unit cost (nullable) is serialized boolean not null default false, notes text, sequence integer not null default 0 ); create table receiveditemserials ( serial instance id bigserial primary key, receipt line id bigint not null references itemreceiptlines(receipt line id) on delete cascade, serial number varchar(255) not null unique, the actual serial number received received at timestamptz not null default current timestamp ); create table purchaseorders ( purchase order id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, msp org placing order vendor org id uuid not null references organizations(org id) on delete restrict, vendor being ordered from po number varchar(50) not null unique, generated sequence order date date not null default current date, expected delivery date date, shipping address location id uuid references locations(location id) on delete set null, where items ship to billing address details jsonb, msp billing address info status varchar(20) default 'draft' check (status in ('draft', 'ordered', 'partialreceipt', 'received', 'cancelled', 'closed')), currency code varchar(3) not null default 'cad', currency for this po subtotal amount numeric(19,4) default 0 00, tax amount numeric(19,4) default 0 00, shipping cost numeric(19,4) default 0 00, total amount numeric(19,4) default 0 00, notes text, created by user id uuid references users(user id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table purchaseorderlineitems ( po line item id bigserial primary key, purchase order id uuid not null references purchaseorders(purchase order id) on delete cascade, product id uuid references products(product id) on delete restrict, description text not null, quantity ordered numeric(19,4) not null, unit cost numeric(19,4) not null, line total numeric(19,4) not null, quantity ordered unit cost quantity received numeric(19,4) default 0 00, notes text, sequence integer not null default 0 ); create table salesorders ( portal order id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, client org placing the order managing org id uuid not null references organizations(org id) on delete cascade, msp org fulfilling the order order number varchar(50) not null unique, order date date not null default current date, status varchar(30) default 'pendingapproval' check (status in ('draft', 'pendingapproval', 'approved', 'pendingprocurement', 'partialshipment', 'shipped', 'invoiced', 'completed', 'cancelled')), agreement id uuid references agreements(agreement id) on delete set null, quote id uuid null, fk constraint added below, links to the originating quote requester user id uuid references users(user id) on delete set null, shipping address location id uuid references locations(location id) on delete set null, billing address details jsonb, currency code varchar(3) not null default 'cad', currency for all monetary amounts on this order subtotal amount numeric(19,4) default 0 00, tax amount numeric(19,4) default 0 00, shipping amount numeric(19,4) default 0 00, total amount numeric(19,4) default 0 00, notes text, notes visible to client internal notes text, internal fulfillment notes approval request id uuid references approvalrequests(approval request id) on delete set null, related project id uuid references projects(project id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated by trigger constraint fk salesorders quote id foreign key (quote id) references quotes(quote id) on delete set null ); comment on column salesorders quote id is 'link to the originating quote that this sales order was converted from, if applicable '; create table salesorderlineitems ( order line item id bigserial primary key, portal order id uuid not null references salesorders(portal order id) on delete cascade, product id uuid references products(product id) on delete restrict, description text not null, quantity numeric(19,4) not null, unit price numeric(19,4) not null, price for this specific line currency code varchar(3) not null default 'cad', currency of unit price and line total line total numeric(19,4) not null, quantity unit price parent line item id bigint references salesorderlineitems(order line item id) on delete cascade, for bundle components is bundle component boolean not null default false, procurement status varchar(30) default 'notneeded' check (procurement status in ('notneeded', 'required', 'ordered', 'received', 'allocated')), quantity procured numeric(19,4) default 0 00, quantity shipped numeric(19,4) default 0 00, notes text, sequence integer not null default 0 ); create table salesorderbundlecomponentstatus ( tracks procurement status for bundle components order line item id bigint not null references salesorderlineitems(order line item id) on delete cascade, the parent bundle line item component product id uuid not null references products(product id) on delete restrict, the specific component product quantity required numeric(19,4) not null, quantity of this component needed per unit of the parent bundle quantity procured numeric(19,4) default 0 00, total quantity procured specifically for this bundle line's needs quantity received numeric(19,4) default 0 00, total quantity received for this bundle line status varchar(30) default 'required', status for this specific component for this bundle line primary key (order line item id, component product id) ); create table salesorderprocurementlinks ( m2m linking so lines to po lines order line item id bigint not null references salesorderlineitems(order line item id) on delete cascade, po line item id bigint not null references purchaseorderlineitems(po line item id) on delete cascade, quantity linked numeric(19,4) not null, how much of po line quantity is allocated to this so line linked at timestamptz not null default current timestamp, primary key (order line item id, po line item id) ); create table shipments ( shipment id uuid primary key default gen random uuid(), sales order id uuid not null references salesorders(portal order id) on delete cascade, org id uuid not null references organizations(org id) on delete cascade, client org receiving the shipment managing org id uuid not null references organizations(org id) on delete cascade, msp org sending the shipment shipment date date not null default current date, carrier varchar(100) null, tracking number varchar(100) null, status varchar(30) not null default 'processing' check (status in ('processing', 'packed', 'shipped', 'delivered', 'cancelled', 'exception')), shipped from warehouse id uuid null references warehouses(warehouse id) on delete set null, warehouse stock was pulled from shipping method varchar(100) null, e g , 'ground', 'overnight' shipping cost charged numeric(19,4) null, actual cost charged to customer for this shipment internal shipping cost numeric(19,4) null, internal cost incurred for this shipment currency code varchar(3) null, currency of cost fields shipping address details jsonb null, snapshot of the shipping address used delivery notes text null, notes for delivery driver or recipient notes text null, internal notes created at timestamptz not null default current timestamp, updated at timestamptz auto updated by trigger ); comment on table shipments is 'tracks outbound shipments related to sales orders '; comment on column shipments status is 'the current status of the shipment process '; create table shipmentlineitems ( shipment line id bigserial primary key, shipment id uuid not null references shipments(shipment id) on delete cascade, sales order line item id bigint not null references salesorderlineitems(order line item id) on delete restrict, link to the so line being shipped product id uuid not null references products(product id) on delete restrict, denormalized for easier access quantity shipped numeric(19,4) not null check (quantity shipped > 0), quantity of this product in this shipment for this so line serial numbers text\[] null, array of serial numbers shipped for this line item in this shipment created at timestamptz not null default current timestamp ); comment on table shipmentlineitems is 'details which products/quantities from a sales order line were included in a specific shipment '; comment on column shipmentlineitems serial numbers is 'array storing serial numbers included in this specific shipment line '; \ ============================================= \ quotes & quoting \ ============================================= \ main table for quote headers create table quotes ( quote id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete restrict, client organization the quote is for managing org id uuid not null references organizations(org id) on delete restrict, msp organization issuing the quote quote number varchar(50) not null, user friendly quote number, unique within the managing org id quote date date not null default current date, expiry date date, optional when the quote expires status varchar(30) not null default 'draft' check (status in ('draft', 'presented', 'needsapproval', 'approved', 'rejected', 'expired', 'converted', 'cancelled')), agreement id uuid null references agreements(agreement id) on delete set null, optional link to a specific client agreement requester user id uuid null references users(user id) on delete set null, commandit user who initiated/created the quote sales rep user id uuid null references users(user id) on delete set null, assigned sales representative primary contact id uuid null references contacts(contact id) on delete set null, primary client contact for this quote shipping address location id uuid null references locations(location id) on delete set null, optional specified shipping location billing address details jsonb, optional snapshot or specific billing address details currency code varchar(3) not null default 'cad', iso 4217 currency code for this quote subtotal amount numeric(19,4) not null default 0 00 check (subtotal amount >= 0), discount amount numeric(19,4) not null default 0 00 check (discount amount >= 0), total discount applied at quote level (if any) tax amount numeric(19,4) not null default 0 00 check (tax amount >= 0), shipping amount numeric(19,4) not null default 0 00 check (shipping amount >= 0), total amount numeric(19,4) not null default 0 00 check (total amount >= 0), calculated subtotal discount + tax + shipping notes text, notes visible to the client internal notes text, internal notes for msp staff approval request id uuid null references approvalrequests(approval request id) on delete set null, link to approval workflow if needed converted sales order id uuid null references salesorders(portal order id) on delete set null, link to the sales order if converted created at timestamptz not null default current timestamp, updated at timestamptz, auto updated by trigger constraint quotes managing org quote number unique unique (managing org id, quote number) ); comment on table quotes is 'stores header information for sales quotes provided to clients '; comment on column quotes org id is 'client organization the quote is for '; comment on column quotes managing org id is 'msp organization issuing the quote '; comment on column quotes quote number is 'user friendly quote number, unique within the managing msp organization '; comment on column quotes status is 'lifecycle status of the quote (draft, presented, approved, rejected, expired, converted, cancelled) '; comment on column quotes agreement id is 'optional link to a specific client agreement that might influence pricing or terms '; comment on column quotes currency code is 'iso 4217 currency code for all monetary amounts on this quote '; comment on column quotes total amount is 'the final calculated total amount for the quote '; comment on column quotes approval request id is 'link to the approval request if this quote requires formal approval '; comment on column quotes converted sales order id is 'link to the sales order generated from this quote, if converted '; \ table for individual line items within a quote create table quotelineitems ( quote line item id bigserial primary key, quote id uuid not null references quotes(quote id) on delete cascade, link back to the parent quote sequence integer not null default 0, display order product id uuid null references products(product id) on delete restrict, link to catalog product description text not null, description (can override product) quantity numeric(19,4) not null check (quantity > 0), unit price numeric(19,4) not null check (unit price >= 0), unit cost numeric(19,4) null check (unit cost >= 0), optional estimated cost discount percentage numeric(5,2) default 0 00 check (discount percentage >= 0 and discount percentage <= 100), line total numeric(19,4) not null check (line total >= 0), calculated quantity unit price (1 discount percentage / 100) is taxable boolean not null default true, tax code id integer null references taxcodes(tax code id) on delete set null, is bundle parent boolean not null default false, is this the main line item representing a bundle/configuration? parent line item id bigint null references quotelineitems(quote line item id) on delete cascade, link to parent bundle/config line if this is a component bundle price display mode varchar(20) null check (bundle price display mode in ('totalprice', 'componentprices')), how to display price for this bundle instance on the quote (applies only if is bundle parent=true) configuration details jsonb null, stores selected configurator options { "attribute name" "selected option value", } (applies only if is bundle parent=true and originated from configurator) notes text notes specific to this line item ); comment on column quotelineitems is bundle parent is 'flags this line item as the main entry for a bundle or configured product; child components link to this via parent line item id '; comment on column quotelineitems parent line item id is 'if this line represents a component, links to the quote line item id of the parent bundle/configured item '; comment on column quotelineitems bundle price display mode is 'controls quote output show only parent total (totalprice) or list component prices (componentprices) applies to parent lines '; comment on column quotelineitems configuration details is 'stores the specific options chosen via a configurator that resulted in this configured bundle being added to the quote '; \ ============================================= \ billing & financials (continued) \ ============================================= create table currencycodes ( currency code char(3) primary key, iso 4217 alpha 3 code (e g , 'cad', 'usd', 'eur') currency name varchar(100) not null, e g , 'canadian dollar', 'us dollar' symbol varchar(5) null, e g , '$', '€' decimal places smallint not null default 2, is active boolean not null default true ); comment on table currencycodes is 'lookup table for iso 4217 currency codes and related information '; create table products ( product id uuid primary key default gen random uuid(), org id uuid null references organizations(org id) on delete cascade, identifier varchar(100) not null, description text not null, product type varchar(30) not null check (product type in ('inventory', 'noninventory', 'service', 'bundle', 'labor', 'expense', 'configurable')), category varchar(100), subcategory varchar(100), manufacturer id uuid null references manufacturers(manufacturer id) on delete set null, manufacturer part number varchar(100), unit price numeric(19,4) default 0 00, default currency code varchar(3) not null default 'cad' references currencycodes(currency code), unit cost numeric(19,4) null, unit of measure varchar(50), default work type id uuid references worktypes(work type id) on delete set null, is serialized boolean not null default false, is bundle boolean not null default false, is configurable boolean not null default false, configurator template id uuid null references configuratortemplates(template id) on delete set null, is billable boolean not null default true, is taxable boolean not null default true, is inactive boolean not null default false, phase out date date null, end of life date date null, end of support date date null, fulfillment template id uuid references tickettemplates(template id) on delete set null, allow backorder boolean not null default true, external id varchar(100), applicable entity types text\[] null, stores \['device', 'user'] or specific type applicable device types text\[] null, stores \['laptop', 'server', ] or null for all device types comparison attributes jsonb null, stores features for comparison modal, e g , {"real time protection" true} is mandatory boolean not null default false, is service required if applicable? is service management enabled boolean not null default false, should this appear on the service management screen? created at timestamptz not null default current timestamp, updated at timestamptz, auto updated by trigger constraint products org identifier unique unique nulls not distinct (org id, identifier), check ((is configurable = true and configurator template id is not null) or (is configurable = false)) ); \ add comments for new/relevant columns comment on column products applicable entity types is 'array indicating if product applies to ''device'', ''user'', or both null implies general applicability based on product type '; comment on column products applicable device types is 'array of specific device types (from devices device type) this product applies to null means applicable to all device types matching applicable entity types '; comment on column products comparison attributes is 'jsonb object storing key features and their values (boolean/text) for display in comparison modals '; comment on column products is mandatory is 'if true, this service must be assigned to applicable entities and cannot be set to ''none'' '; comment on column products is service management enabled is 'if true and product type is ''service'', this product will appear as an option on the service management assignment screen '; \ add indexes for performance on new/relevant columns create index idx products category on products(category); create index idx products product type on products(product type); create index idx products svc mgmt enabled on products(is service management enabled) where is service management enabled = true; \ consider gin index if querying arrays frequently \ create index idx products applicable entity types on products using gin (applicable entity types); \ create index idx products applicable device types on products using gin (applicable device types); create table productvendorcosts ( product vendor cost id uuid primary key default gen random uuid(), product id uuid not null references products(product id) on delete cascade, vendor org id uuid not null references organizations(org id) on delete restrict, link to the vendor/distributor organization vendor sku varchar(100) null, the sku or part number used by this specific vendor unit cost numeric(19,4) not null, cost of the product from this vendor currency code varchar(3) not null, iso 4217 currency code for the unit cost (e g , 'cad', 'usd') minimum order quantity numeric(19,4) default 1 00, unit of measure varchar(50), unit of measure for the cost (if different from product standard) is preferred source boolean not null default false, is this the default vendor to source from? last checked at timestamptz, when this cost was last verified/updated from the vendor feed/api source url text null, optional url to the product page on the vendor's site notes text, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated by trigger constraint productvendorcosts product vendor unique unique (product id, vendor org id) ); create index idx productvendorcosts product id on productvendorcosts(product id); create index idx productvendorcosts vendor org id on productvendorcosts(vendor org id); create table pricingrulesets ( rule set id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(150) not null, description text, currency code varchar(3) not null, is default for org boolean not null default false, is active boolean not null default true, rounding rules definition jsonb null, structured definition for rounding rules minimum margin percent numeric(5,2) null check (minimum margin percent >= 0 and minimum margin percent < 100), minimum margin to protect during rounding created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (org id, name) ); comment on table pricingrulesets is 'defines named collections of specific pricing rules, including rounding and minimum margin settings '; comment on column pricingrulesets rounding rules definition is 'jsonb array defining tiered rounding rules example \[{"max price threshold" 100, "rounding type" "nearest", "rounding multiple" 10, "apply ending adjustment" "subtract0 01"}, {"rounding type" "none"}]'; comment on column pricingrulesets minimum margin percent is 'optional minimum margin % if rounding down violates this margin, rounding is adjusted/skipped '; create table pricingrules ( rule id uuid primary key default gen random uuid(), rule set id uuid not null references pricingrulesets(rule set id) on delete cascade, priority integer not null default 0, execution order within set (lower runs first, first match typically wins) name varchar(255) null, optional name for the specific rule is active boolean not null default true, is this rule currently active? effective start date date null, optional start date for rule validity effective end date date null, optional end date for rule validity \ filter criteria (rule applies if all non null filters match) filter product id uuid null references products(product id) on delete cascade, specific product filter product category varchar(100) null, specific product category filter product manufacturer id uuid null references manufacturers(manufacturer id) on delete set null, specific manufacturer filter target org id uuid null references organizations(org id) on delete cascade, specific client org filter target org type id integer null references organizationtypes(org type id) on delete restrict, specific org type filter target location id uuid null references locations(location id) on delete cascade, specific location filter min quantity numeric(19,4) null, minimum quantity for this rule to apply (volume break) \ pricing calculation definition calculation method varchar(30) not null check (calculation method in ('fixedprice', 'costpluspercent', 'costplusamount', 'listpriceminuspercent')), cost basis varchar(30) null check (cost basis in ('standardcost', 'preferredvendorcost', 'specificvendorcost')), required if method is costplus cost basis vendor org id uuid null references organizations(org id) on delete restrict, required if cost basis='specificvendorcost' price or markup value numeric(19,4) not null, the fixed price, markup %, markup amount, or discount % created at timestamptz not null default current timestamp, updated at timestamptz, auto updated check ((calculation method like 'costplus%' and cost basis is not null) or (calculation method not like 'costplus%')), cost basis needed for costplus check ((cost basis = 'specificvendorcost' and cost basis vendor org id is not null) or (cost basis != 'specificvendorcost' or cost basis is null)) vendor needed for specificvendorcost ); comment on table pricingrules is 'defines individual pricing rules within a rule set, including filters and calculation methods '; comment on column pricingrules priority is 'determines rule evaluation order within a set (lower number evaluated first) '; comment on column pricingrules filter product id is 'applies rule only if this specific product matches '; comment on column pricingrules filter min quantity is 'applies rule only if line item quantity meets or exceeds this minimum '; comment on column pricingrules calculation method is 'how the selling price is calculated (fixed, cost+, list ) '; comment on column pricingrules cost basis is 'which cost to use for costplus calculations (standard product cost, preferred vendor cost, specific vendor cost) '; comment on column pricingrules cost basis vendor org id is 'the specific vendor org id if cost basis is specificvendorcost '; comment on column pricingrules price or markup value is 'the value used in the calculation (fixed price, percentage, or fixed amount) '; create table productbundlecomponents ( bundle component id bigserial primary key, parent product id uuid not null references products(product id) on delete cascade, the product defined as the bundle child product id uuid not null references products(product id) on delete cascade, the product included in the bundle quantity numeric(19,4) not null default 1 check (quantity > 0), quantity of the child item per bundle unit is optional boolean not null default false, can this component be optionally included/excluded by the user? (mainly relevant for configurators/templates) default included boolean not null default true, if optional, is it selected by default? unique (parent product id, child product id) ); comment on table productbundlecomponents is 'defines the component products included within a bundle product in the catalog '; comment on column productbundlecomponents parent product id is 'fk to the products table identifying the main bundle item '; comment on column productbundlecomponents child product id is 'fk to the products table identifying a component within the bundle '; comment on column productbundlecomponents quantity is 'default quantity of the child product included for each unit of the parent bundle '; create table configuratortemplates ( template id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(255) not null, description text, base product id uuid null references products(product id) on delete set null, pricing rule set id uuid null references pricingrulesets(rule set id) on delete set null, specific pricing rules for components is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, unique(org id, name) ); comment on column configuratortemplates pricing rule set id is 'optional link to a pricingruleset used specifically to price components selected via this configurator, overriding standard pricing hierarchy '; create table configuratorattributegroups ( group id uuid primary key default gen random uuid(), template id uuid not null references configuratortemplates(template id) on delete cascade, name varchar(100) not null, e g , "core components", "storage options" description text, image url text null, optional url for a group icon/image sequence order integer not null default 0, unique (template id, name) ); create table configuratorattributes ( attribute id uuid primary key default gen random uuid(), template id uuid not null references configuratortemplates(template id) on delete cascade, group id uuid null references configuratorattributegroups(group id) on delete set null, name varchar(150) not null, e g , "memory (ram)", "operating system" description text, attribute type varchar(20) not null default 'singleselect' check (attribute type in ('singleselect', 'multiselect', 'quantity')), display hint varchar(20) null check (display hint in ('radio', 'checkbox', 'dropdown', 'slider', 'numberinput')), image url text null, optional url for an attribute icon/image is required boolean not null default true, sequence order integer not null default 0, unique(template id, name) ); comment on column configuratorattributes attribute type is 'defines the logical selection rule singleselect (choose one), multiselect (choose zero or more), quantity (enter a number) '; comment on column configuratorattributes display hint is 'optional suggestion for ui rendering (e g , radio/dropdown for singleselect, checkbox for multiselect, numberinput/slider for quantity) '; reate table configuratoroptions ( option id uuid primary key default gen random uuid(), attribute id uuid not null references configuratorattributes(attribute id) on delete cascade, display text varchar(255) not null, related product id uuid null references products(product id) on delete set null, link to the actual product sku quantity modifier numeric(19,4) not null default 1, quantity of related product id added image url text null, is default option boolean not null default false, is available boolean not null default true, sequence order integer not null default 0, created at timestamptz not null default current timestamp, updated at timestamptz, unique (attribute id, display text) ); comment on column configuratoroptions related product id is 'links option to a product price is determined by applying pricing rules to this product '; comment on column configuratoroptions quantity modifier is 'quantity of the related product id added when this option is selected '; comment on column configuratoroptions price adjustment is 'price difference relative to the default option for this attribute (can be positive or negative) '; create table configuratorrules ( rule id uuid primary key default gen random uuid(), template id uuid not null references configuratortemplates(template id) on delete cascade, rule applies to the whole template name varchar(255), optional name for the rule description text, explanation of the rule rule type varchar(20) not null check (rule type in ('requirement', 'exclusion', 'recommendation', 'priceadjustment', 'quantityconstraint')), condition logic jsonb not null, defines trigger e g , { "operator" "and", "conditions" \[ {"attribute id" "uuid", "selected option id" "uuid"}, ] } action logic jsonb not null, defines action e g , { "action" "require", "option id" "uuid" } or { "action" "exclude", "attribute id" "uuid"} or { "action" "set quantity", "option id" "uuid", "quantity" 2} is active boolean not null default true, error message text null, message to display to user if rule is violated (especially for exclusions) created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); comment on table configuratorrules is 'stores rules defining dependencies, exclusions, or price adjustments between selected configurator options '; comment on column configuratorrules condition logic is 'json structure defining the conditions (selected options) that trigger this rule '; comment on column configuratorrules action logic is 'json structure defining the action to take (require/exclude option/attribute, adjust price, set quantity) when conditions are met '; create table mspcountrydistributorprefs ( msp country dist pref id bigserial primary key, msp org id uuid not null references organizations(org id) on delete cascade, the msp setting the preference country code varchar(2) not null, iso 3166 1 alpha 2 country code preferred vendor org id uuid not null references organizations(org id) on delete restrict, link to the preferred vendor org priority integer not null default 0, lower number = higher preference last updated timestamptz default current timestamp, unique (msp org id, country code, preferred vendor org id), unique (msp org id, country code, priority) ensure unique priority per country for ordering ); comment on table mspcountrydistributorprefs is 'allows msps to define preferred vendors/distributors per country, with priority '; create table worktypes ( work type id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, msp defining the work type name varchar(100) not null, e g , 'onsite support', 'remote support', 'project labor', 'travel time' description text, is billable boolean not null default true, does time logged with this type typically get billed? (rate sheet determines rate) is active boolean not null default true, can this work type be used? external id varchar(100), id from an external system (e g , psa integration) created at timestamptz not null default current timestamp, updated at timestamptz, auto updated by trigger constraint worktypes org name unique unique(org id, name) ); create table ratesheets ( rate sheet id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(100) not null, is default boolean not null default false, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint ratesheets org name unique unique(org id, name) ); create table ratesheetlines ( rate sheet line id bigserial primary key, rate sheet id uuid not null references ratesheets(rate sheet id) on delete cascade, work type id uuid not null references worktypes(work type id) on delete restrict, rate modifier varchar(50) default 'standard' check (rate modifier in ('standard', 'overtime', 'afterhours', 'holiday', 'weekend')), billing rate numeric(19,4) not null, minimum charge minutes integer default 0, billing increment minutes integer default 1, surcharge amount numeric(19,4) default 0 00, effective start date date default current date, effective end date date, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (rate sheet id, work type id, rate modifier, effective start date) ); create table holidaysets ( holiday set id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(100) not null, description text, is default boolean not null default false, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique(org id, name) ); create table holidaydates ( holiday date id bigserial primary key, holiday set id uuid not null references holidaysets(holiday set id) on delete cascade, holiday date date not null, the actual date the holiday falls on (e g , jan 1st) name varchar(100) not null, e g , "new year's day", "canada day" observed on date date null, if observed on different date (e g , the monday after), store that date here null if observed on holiday date is observed as closed boolean not null default true, does this observed date count as non working for sla/scheduling? default yes created at timestamptz not null default current timestamp, \ unique constraint might need adjustment depending on how duplicates are handled, \ but generally should be unique per set for the date that matters (observed or actual) unique (holiday set id, holiday date) base uniqueness on the statutory date per set ); create table agreements ( agreement id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, managing org id uuid not null references organizations(org id) on delete cascade, name varchar(255) not null, start date date not null, end date date, agreement type varchar(50), status varchar(20) default 'active' check (status in ('draft', 'active', 'expired', 'cancelled')), rate sheet id uuid references ratesheets(rate sheet id) on delete set null, business hours id uuid references businesshours(business hours id) on delete set null, holiday set id uuid references holidaysets(holiday set id) on delete set null, travel billing policy id uuid null references travelbillingpolicies(travel billing policy id) on delete set null, travel billing approval workflow id uuid null references approvalworkflowdefinitions(definition id) on delete set null, pricing rule set id uuid null references pricingrulesets(rule set id) on delete set null, pricing rules specific to non addition items in this agreement allow overage boolean default true, bill travel separately boolean default true, default so client approval varchar(20) default 'none' check (default so client approval in ('none', 'required', 'autoapproved')), default so approval threshold numeric(19,4), onsite billing ref location id uuid references locations(location id) on delete set null, onsite billing distance km integer, onsite billing distro group id uuid references distributiongroups(distribution group id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (org id, name) ); comment on column agreements pricing rule set id is 'optional link to a pricingruleset applying to items quoted/ordered under this agreement (lower priority than agreementadditions pricing) '; comment on column agreements onsite billing distro group id is 'distribution group to get approval for onsite billing if distance check returns billable '; create table agreementadditions ( addition id bigserial primary key, agreement id uuid not null references agreements(agreement id) on delete cascade, product id uuid references products(product id) on delete set null, description text not null, quantity included numeric(19,4), unit of measure varchar(50), unit price override numeric(19,4), recurring cycle varchar(20) check (recurring cycle in ('monthly', 'quarterly', 'annually', 'onetime')), recurring fee numeric(19,4) default 0 00, is taxable boolean default true, apply to all locations boolean default true, target location id uuid references locations(location id) on delete cascade, \ distance threshold fields for this specific addition/location target distance threshold km integer null, threshold (can be null if not applicable) distance surcharge product id uuid null references products(product id) on delete set null, product to bill if threshold exceeded distance surcharge flat amount numeric(19,4) null, flat amount if no product distance threshold applies to varchar(20) null check (distance threshold applies to in ('traveltoclientsite', 'travelfromclientsite', 'both')), which leg? measure distance from varchar(30) null check (measure distance from in ('agreementreferencepoints', 'previoussite', 'techstartlocation')), how to measure? created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table agreementdistancereferencelocations ( agreement id uuid not null references agreements(agreement id) on delete cascade, location id uuid not null references locations(location id) on delete cascade, primary key (agreement id, location id) ); create table agreementtemplates ( agreement template id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, msp defining the template name varchar(255) not null, description text, agreement type varchar(50), default type for agreements created from this template rate sheet id uuid null references ratesheets(rate sheet id) on delete set null, business hours id uuid null references businesshours(business hours id) on delete set null, holiday set id uuid null references holidaysets(holiday set id) on delete set null, travel billing policy id uuid null references travelbillingpolicies(travel billing policy id) on delete set null, default additions jsonb null, simplified store default additions as json { "additions" \[ { "product identifier" "sku123", "description" "managed antivirus", "quantity" 10, "recurring cycle" "monthly", "recurring fee" 5 00 }, ] } default sla ids uuid\[] null, array of sla ids to apply by default default so client approval varchar(20) default 'none' check (default so client approval in ('none', 'required', 'autoapproved')), default so approval threshold numeric(19,4), notes text, is active boolean not null default true, can this template be used? created at timestamptz not null default current timestamp, updated at timestamptz, auto updated by trigger constraint agreementtemplates org name unique unique(org id, name) ); create table agreementtemplatedistancereferencelocations ( agreement template id uuid not null references agreementtemplates(agreement template id) on delete cascade, location id uuid not null references locations(location id) on delete cascade, primary key (agreement template id, location id) ); create table slas ( sla id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(150) not null, description text, service target varchar(50) not null check (service target in ('responsetime', 'resolutiontime', 'uptimepercentage')), metric varchar(50) not null check (metric in ('minutes', 'hours', 'percentage')), target value numeric(10, 2) not null, warning threshold percent integer not null default 80 check (warning threshold percent > 0 and warning threshold percent < 100), percentage of time elapsed before 'atrisk' business hours id uuid null references businesshours(business hours id) on delete set null, applies to filter jsonb null, filter criteria for tickets { "priority" \["critical", "high"], "type" \["incident"] } is default boolean not null default false, is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (org id, name) ); create table agreementslas ( agreement sla id bigserial primary key, agreement id uuid not null references agreements(agreement id) on delete cascade, sla id uuid not null references slas(sla id) on delete cascade, assigned at timestamptz not null default current timestamp, unique (agreement id, sla id) ); create table slabreachlog ( breach log id bigserial primary key, ticket id bigint not null references tickets(ticket id) on delete cascade, org id uuid not null references organizations(org id) on delete cascade, denormalized for easier reporting sla id uuid not null references slas(sla id) on delete cascade, the sla definition that was breached breach type varchar(20) not null check (breach type in ('response', 'resolution')), which target was missed sla target value minutes integer null, target time in minutes (denormalized from sla for context) sla business hours id uuid null references businesshours(business hours id) on delete set null, business hours applied (denormalized) due timestamp timestamptz not null, when the sla target was due (utc) actual timestamp timestamptz null, when the action (response/resolution) actually occurred (utc) breach duration seconds integer null, calculated duration of the breach in seconds (actual due) logged at timestamptz not null default current timestamp when this breach record was created ); comment on table slabreachlog is 'logs instances where ticket response or resolution times breached defined sla targets '; comment on column slabreachlog breach type is 'indicates whether the response time or resolution time target was breached '; comment on column slabreachlog breach duration seconds is 'calculated time difference between actual completion and the due time, in seconds '; create table usercostrates ( user cost rate id bigserial primary key, user id uuid not null references users(user id) on delete cascade, effective date date not null default current date, hourly cost rate numeric(19,4) not null default 0 00, salary amount numeric(19,4), salary period varchar(20) check (salary period in ('annual', 'monthly', 'biweekly')), is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (user id, effective date) ); create table timeentries ( time entry id bigserial primary key, user id uuid not null references users(user id) on delete restrict, org id uuid not null references organizations(org id) on delete cascade, client org context ticket id bigint null references tickets(ticket id) on delete set null, link to the primary ticket project task id bigint null references projecttasks(project task id) on delete set null, link if for a project task schedule entry id bigint null references technicianschedules(schedule entry id) on delete set null, link to the scheduled block related chat channel id uuid null references chatchannels(channel id) on delete set null, link to chat channel if time tracked via chat agreement id uuid null references agreements(agreement id) on delete set null, agreement context for billing work type id uuid not null references worktypes(work type id) on delete restrict, type of work performed start time utc timestamptz not null, timer start time end time utc timestamptz null, timer end time (null if running) duration seconds integer null, calculated duration (end start) can be calculated on update/query notes text null, tech's notes about the work done during this entry internal notes text null, internal only notes is billable boolean not null default true, should this entry appear on an invoice? is travel boolean not null default false, is this specifically travel time? travel distance km numeric(10,2) null, travel from location id uuid null references locations(location id) on delete set null, travel to location id uuid null references locations(location id) on delete set null, calculated cost numeric(19,4) null, calculated internal cost (duration usercostrate) calculated rate numeric(19,4) null, calculated billing rate (from ratesheet based on worktype/agreement) calculated bill amount numeric(19,4) null, calculated bill amount (duration rate) invoice line item id bigint null references invoicelineitems(invoice line item id) on delete set null, link after invoicing created at timestamptz not null default current timestamp, updated at timestamptz, auto updated check (end time utc is null or end time utc >= start time utc) ); comment on column timeentries related chat channel id is 'if this time entry was generated from activity within a specific chat channel, links to that channel '; comment on column timeentries end time utc is 'timer end time null indicates the timer is currently running for this entry '; comment on column timeentries duration seconds is 'calculated duration (end time utc start time utc) may be null if timer is running '; create table expenseentries ( expense entry id bigserial primary key, user id uuid not null references users(user id) on delete restrict, org id uuid not null references organizations(org id) on delete cascade, client org expense date date not null, ticket id bigint references tickets(ticket id) on delete set null, project id uuid references projects(project id) on delete set null, schedule entry id bigint references technicianschedules(schedule entry id) on delete set null, agreement id uuid references agreements(agreement id) on delete set null, expense type varchar(100), e g , 'hotel', 'flight', 'perdiem', 'rentalcar', 'meals' description text not null, quantity numeric(10, 2) not null default 1 00, for items like 'days' of per diem unit amount numeric(19,4), cost per unit (e g , per diem rate) total amount numeric(19,4) not null, currency code varchar(3) not null default 'cad', billing option varchar(15) not null default 'billable' check (billing option in ('billable', 'donotbill', 'nocharge')), payment method varchar(50), 'company card', 'personal' requires reimbursement boolean not null default false, explicit flag, could be generated based on payment method markup percentage numeric(5, 2) null, optional markup % for this line billable amount numeric(19,4) null, calculated amount to invoice (total amount + markup) invoice line item id bigint references invoicelineitems(invoice line item id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz auto updated \ consider adding check constraint total amount = quantity unit amount where unit amount is not null ); create table expenseentryattachments ( expense entry id bigint not null references expenseentries(expense entry id) on delete cascade, attachment id uuid not null references attachments(attachment id) on delete cascade, attached at timestamptz not null default current timestamp, primary key (expense entry id, attachment id) ); create table taxcodes ( tax code id serial primary key, org id uuid not null references organizations(org id) on delete cascade, name varchar(50) not null, rate percent numeric(5,3) not null, is compound boolean not null default false, tax agency name varchar(100), tax number varchar(50), is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique(org id, name) ); create table invoices ( invoice id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, client org being invoiced managing org id uuid not null references organizations(org id) on delete cascade, msp org issuing the invoice invoice number varchar(50) not null unique, invoice date date not null default current date, due date date, agreement id uuid references agreements(agreement id) on delete set null, status varchar(20) default 'draft' check (status in ('draft', 'sent', 'paid', 'partial', 'overdue', 'void')), currency code varchar(3) not null default 'cad', currency for all monetary amounts on this invoice subtotal amount numeric(19,4) default 0 00, tax amount numeric(19,4) default 0 00, total amount numeric(19,4) default 0 00, amount paid numeric(19,4) default 0 00, balance due numeric(19,4) default 0 00, typically total amount amount paid notes text, notes visible to the client internal notes text, notes only visible internally sent at timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz auto updated by trigger ); create table invoicelineitems ( invoice line item id bigserial primary key, invoice id uuid not null references invoices(invoice id) on delete cascade, sequence integer not null default 0, source type varchar(20) check (source type in ('timeentry', 'expenseentry', 'product', 'agreementaddition', 'manual')), source id varchar(50), description text not null, quantity numeric(19,4) not null, unit price numeric(19,4) not null, line total numeric(19,4) not null, is taxable boolean default true, tax code id integer references taxcodes(tax code id) on delete set null, created at timestamptz not null default current timestamp ); create table payments ( payment id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, invoice id uuid references invoices(invoice id) on delete set null, payment date date not null default current date, amount numeric(19,4) not null, currency code varchar(3) not null default 'cad', payment method varchar(50), reference number varchar(100), notes text, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table assetfinancials ( asset financial id bigserial primary key, target entity type varchar(50) not null check (target entity type in ('device', 'peripheral', 'softwarelicense', 'equipmentasset')), target entity id uuid not null, purchase order id uuid references purchaseorders(purchase order id) on delete set null, purchase date date, purchase cost numeric(19,4), currency code varchar(3), depreciation method varchar(50), depreciation start date date, useful life years integer, salvage value numeric(19,4), current book value numeric(19,4), last depreciation calc date date, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (target entity type, target entity id) ); create table assetleases ( lease id uuid primary key default gen random uuid(), target entity type varchar(50) not null check (target entity type in ('device', 'peripheral', 'equipmentasset')), target entity id uuid not null, lessor vendor org id uuid references organizations(org id) on delete set null, lease start date date not null, lease end date date not null, lease term months integer, monthly payment numeric(19,4), buyout option varchar(50), buyout amount numeric(19,4), currency code varchar(3) not null default 'cad', currency of payment/buyout amounts lease account number varchar(100), notes text, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated by trigger unique (target entity type, target entity id, lease start date) ); create table assetleaseattachments ( lease id uuid not null references assetleases(lease id) on delete cascade, attachment id uuid not null references attachments(attachment id) on delete cascade, description text, attached at timestamptz not null default current timestamp, primary key (lease id, attachment id) ); \ ============================================= \ scheduling & availability \ ============================================= create table technicianavailabilitytemplates ( template id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(100) not null unique, description text, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table technicianavailabilityslots ( slot id bigserial primary key, template id uuid not null references technicianavailabilitytemplates(template id) on delete cascade, day of week integer not null check (day of week >= 0 and day of week <= 6), start time local time not null, end time local time not null, check (end time local > start time local) ); create table useravailability ( user id uuid primary key references users(user id) on delete cascade, availability template id uuid references technicianavailabilitytemplates(template id) on delete set null, time zone varchar(100) not null, updated at timestamptz auto updated ); create table technicianschedules ( schedule entry id bigserial primary key, user id uuid null references users(user id) on delete cascade, made nullable assigned vendor org id uuid null references organizations(org id) on delete set null, link to vendor org schedule type varchar(30) not null check (schedule type in ('workorder', 'appointment', 'timeoff', 'travel', 'meeting', 'other', 'externaldispatch')), activity type varchar(100), ticket id bigint references tickets(ticket id) on delete set null, project task id bigint references projecttasks(project task id) on delete set null, time off request id uuid references timeoffrequests(request id) on delete set null, originating field request id uuid references fieldservicerequests(field service request id) on delete set null, start time utc timestamptz not null, end time utc timestamptz not null, actual start time utc timestamptz, actual end time utc timestamptz, title varchar(255), description text, location id uuid references locations(location id) on delete set null, bench id uuid references workbenches(bench id) on delete set null, related device id uuid references devices(device id) on delete set null, external vendor reference varchar(100), vendor's job # is all day boolean not null default false, status varchar(20) default 'scheduled' check (status in ('scheduled', 'confirmed', 'traveling', 'inprogress', 'onhold', 'completed', 'cancelled', 'rescheduled', 'vendoracknowledged', 'vendorcomplete')), reminder time minutes integer, is shiftable boolean default true, optimized sequence order integer, estimated duration seconds integer, travel time before seconds integer, travel time after seconds integer, created at timestamptz not null default current timestamp, updated at timestamptz, check (end time utc > start time utc), constraint chk schedule assignee check (user id is not null or assigned vendor org id is not null) must be assigned to user or vendor \ constraint prevent tech overlaps exclude using gist ( ) needs gist index ); create table travellogentries ( travel log id bigserial primary key, schedule entry id bigint references technicianschedules(schedule entry id) on delete cascade, technician user id uuid not null references users(user id) on delete cascade, travel type varchar(20) not null check (travel type in ('tosite', 'fromsite', 'tooffice', 'tohome', 'betweensites')), start time utc timestamptz not null, end time utc timestamptz, start location id uuid references locations(location id) on delete set null, end location id uuid references locations(location id) on delete set null, start latitude numeric(9, 6), start longitude numeric(9, 6), end latitude numeric(9, 6), end longitude numeric(9, 6), calculated distance km numeric(10, 2), data source varchar(15) default 'estimated' check (data source in ('gps', 'manual', 'estimated')), billing status varchar(30) default 'pendingevaluation' check (billing status in ('pendingevaluation', 'billedtime', 'billedmileage', 'billedflatrate', 'billedsplit', 'nonbillablefirstlast', 'nonbillablepolicy', 'manualreviewrequired')), applied policy id uuid references travelbillingpolicies(travel billing policy id) on delete set null, which policy was used policy evaluation notes text, explanation of decision (e g , "first trip, non billable") billed ticket id bigint references tickets(ticket id) on delete set null, primary ticket billed to (if not split) billed split ticket id 1 bigint references tickets(ticket id) on delete set null, ticket 1 if split billing billed split ticket id 2 bigint references tickets(ticket id) on delete set null, ticket 2 if split billing billed time entry id bigint references timeentries(time entry id) on delete set null, link to time entry if billed by time billed product usage id bigint references ticketproductsused(ticket product id) on delete set null, link to product usage if billed by mileage/flatrate created at timestamptz not null default current timestamp when the travel log entry was created ); create table userexternalcalendarevents ( external event id varchar(255) primary key, user id uuid not null references users(user id) on delete cascade, start time utc timestamptz not null, end time utc timestamptz not null, subject varchar(255), is all day boolean not null default false, show as varchar(20), 'free', 'busy', 'tentative', 'outofoffice' last synced at timestamptz not null default current timestamp, source varchar(50) ); create table schedulingrequests ( request id uuid primary key default gen random uuid(), ticket id bigint not null references tickets(ticket id) on delete cascade, requester user id uuid references users(user id) on delete set null, requester email varchar(255), token varchar(64) not null unique, expires at timestamptz not null, status varchar(20) default 'sent' check (status in ('sent', 'viewed', 'scheduled', 'expired', 'cancelled')), available slots jsonb, selected slot start timestamptz, selected slot end timestamptz, related schedule entry id bigint references technicianschedules(schedule entry id) on delete set null, sent at timestamptz not null default current timestamp, viewed at timestamptz, scheduled at timestamptz, cancelled at timestamptz ); create table timeofftypes ( time off type id serial primary key, org id uuid not null references organizations(org id) on delete cascade, name varchar(100) not null unique, description text, is paid boolean not null default true, debits allotment boolean not null default true, is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table usertimeoffallotments ( allotment id bigserial primary key, user id uuid not null references users(user id) on delete cascade, time off type id integer not null references timeofftypes(time off type id) on delete cascade, year integer not null, allotted hours numeric(10,2) not null default 0 00, used hours numeric(10,2) not null default 0 00, remaining hours numeric(10,2) generated always as (allotted hours used hours) stored, calculated created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique(user id, time off type id, year) ); create table timeoffrequests ( request id uuid primary key default gen random uuid(), user id uuid not null references users(user id) on delete cascade, time off type id integer not null references timeofftypes(time off type id) on delete restrict, start time utc timestamptz not null, end time utc timestamptz not null, requested hours numeric(10,2) not null, reason text, status varchar(20) default 'pending' check (status in ('pending', 'approved', 'rejected', 'cancelled')), approval request id uuid references approvalrequests(approval request id) on delete set null, processed by user id uuid references users(user id) on delete set null, processed at timestamptz, notes text, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated check (end time utc > start time utc) ); create table maintenancewindows ( maintenance window id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, org defining the window name varchar(150) not null, e g , "monthly server patching", "network freeze q4" reason text, start time utc timestamptz not null, end time utc timestamptz not null, is recurring boolean not null default false, recurrence rule text null, icalendar rrule string or similar representation scope type varchar(30) not null check (scope type in ('global', 'organization', 'location', 'device', 'taggroup')), scope of the window's impact scope entity id uuid null, links to org, location, device based on scope type null if global or taggroup target tag ids uuid\[] null, array of tag ids if scope type is taggroup pauses sla timers boolean not null default true, does this window pause applicable sla clocks? restricts scheduling boolean not null default false, prevent scheduling during this window? restricts changes boolean not null default false, prevent change implementations during this window? (overlap with blackoutperiods?) is active boolean not null default true, created by user id uuid references users(user id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated by trigger check (end time utc > start time utc), check ((scope type != 'taggroup' and scope entity id is not null) or (scope type = 'taggroup' and target tag ids is not null) or (scope type = 'global' and scope entity id is null)), check ((is recurring = true and recurrence rule is not null) or (is recurring = false)) ); comment on table maintenancewindows is 'defines scheduled maintenance periods that can affect sla calculations, scheduling, or change implementations '; comment on column maintenancewindows scope type is 'defines the scope affected by this maintenance window (global, specific org/location/device, or devices/users matching specific tags) '; comment on column maintenancewindows target tag ids is 'array of tag uuids; applies if scope type is taggroup '; comment on column maintenancewindows pauses sla timers is 'if true, sla timers for affected entities will pause during this window '; \ ============================================= \ documentation & knowledge management \ ============================================= create table documenttypes ( document type id serial primary key, org id uuid references organizations(org id) on delete cascade, name varchar(100) not null, description text, is system defined boolean not null default false, is portal article boolean not null default false, unique nulls not distinct (org id, name) ); create table documents ( document id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, title varchar(255) not null, document type id integer not null references documenttypes(document type id) on delete restrict, current revision id uuid, fk status varchar(20) default 'draft' check (status in ('draft', 'published', 'archived')), visibility varchar(20) default 'internal' check (visibility in ('internal', 'specificroles', 'specificorgs', 'public')), allowed role ids uuid\[], allowed org ids uuid\[], tags text\[], source template id uuid references documenttemplates(template id) on delete set null, source document id uuid references documents(document id) on delete set null, thumbnail url text, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated published at timestamptz, archived at timestamptz ); create table documentrevisions ( revision id uuid primary key default gen random uuid(), document id uuid not null references documents(document id) on delete cascade, revision number integer not null, content type varchar(20) not null default 'markdown' check (content type in ('markdown', 'html', 'jsonschema')), content text not null, created by user id uuid references users(user id) on delete set null, created at timestamptz not null default current timestamp, change summary text, unique (document id, revision number) ); \ add fk constraint now that documentrevisions exists alter table documents add constraint fk docs current revision foreign key (current revision id) references documentrevisions(revision id) on delete set null; create table documentrelateditems ( document id uuid not null references documents(document id) on delete cascade, target entity type varchar(50) not null, target entity id varchar(50) not null, relationship type varchar(50) default 'related', added at timestamptz not null default current timestamp, primary key (document id, target entity type, target entity id) ); create table documenttemplates ( template id uuid primary key default gen random uuid(), org id uuid references organizations(org id) on delete cascade, name varchar(255) not null, description text, document type id integer not null references documenttypes(document type id) on delete restrict, content type varchar(20) not null default 'markdown', template content text not null, variables schema jsonb, is system defined boolean not null default false, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique nulls not distinct (org id, name) ); create table documentreviewrequests ( review request id bigserial primary key, document id uuid not null references documents(document id) on delete cascade, revision id uuid null references documentrevisions(revision id) on delete set null, specific revision flagged, if any requesting user id uuid not null references users(user id) on delete set null, who flagged it request timestamp timestamptz not null default current timestamp, reason text not null, why it needs review suggested changes text, optional user suggestion status varchar(20) not null default 'open' check (status in ('open', 'inprogress', 'resolved', 'rejected')), resolver user id uuid null references users(user id) on delete set null, who handled the review resolved at timestamptz null, resolution notes text, outcome notes related ticket id bigint null references tickets(ticket id) on delete set null optional ticket that triggered the review ); create table articlecategories ( article category id serial primary key, org id uuid references organizations(org id) on delete cascade, name varchar(100) not null, description text, parent category id integer references articlecategories(article category id) on delete set null, sort order integer default 0, is active boolean not null default true, unique nulls not distinct (org id, name) ); create table documentarticlecategories ( document id uuid not null references documents(document id) on delete cascade, article category id integer not null references articlecategories(article category id) on delete cascade, primary key (document id, article category id) ); \ ============================================= \ learning management system (lms) \ ============================================= create table courses ( course id uuid primary key default gen random uuid(), org id uuid references organizations(org id) on delete cascade, title varchar(255) not null, description text, category varchar(100), estimated duration seconds integer, difficulty level varchar(20) check (difficulty level in ('beginner', 'intermediate', 'advanced')), thumbnail url text, is published boolean not null default false, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table quizdefinitions ( quiz definition id uuid primary key default gen random uuid(), org id uuid references organizations(org id) on delete cascade, name varchar(255) not null, description text, passing score percent integer not null check (passing score percent >= 0 and passing score percent <= 100), time limit seconds integer, randomize questions boolean not null default false, randomize answers boolean not null default false, max attempts integer, show results varchar(20) default 'afterattempt' check (show results in ('afterattempt', 'afterpassing', 'never')), created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table courselessons ( lesson id uuid primary key default gen random uuid(), course id uuid not null references courses(course id) on delete cascade, sequence order integer not null default 0, title varchar(255) not null, lesson type varchar(20) not null check (lesson type in ('htmlcontent', 'video', 'quiz', 'attachment', 'externallink')), html content text, video url text, external link url text, quiz definition id uuid references quizdefinitions(quiz definition id) on delete set null, attachment id uuid references attachments(attachment id) on delete set null, estimated duration seconds integer, is required boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table quizquestions ( question id uuid primary key default gen random uuid(), quiz definition id uuid not null references quizdefinitions(quiz definition id) on delete cascade, sequence order integer not null default 0, question text text not null, question type varchar(20) not null check (question type in ('multiplechoicesingle', 'multiplechoicemulti', 'truefalse', 'fillinblank')), points integer not null default 1, feedback correct text, feedback incorrect text ); create table quizquestionanswers ( answer id uuid primary key default gen random uuid(), question id uuid not null references quizquestions(question id) on delete cascade, answer text text not null, is correct boolean not null default false, sort order integer not null default 0 ); create table userquizattempts ( attempt id uuid primary key default gen random uuid(), user id uuid not null references users(user id) on delete cascade, lesson id uuid not null references courselessons(lesson id) on delete cascade, quiz definition id uuid not null references quizdefinitions(quiz definition id) on delete cascade, attempt number integer not null, start time timestamptz not null default current timestamp, end time timestamptz, score achieved integer, score possible integer, passed boolean, status varchar(20) default 'inprogress' check (status in ('inprogress', 'completed')) ); create table userquizattemptanswers ( attempt answer id bigserial primary key, attempt id uuid not null references userquizattempts(attempt id) on delete cascade, question id uuid not null references quizquestions(question id) on delete cascade, selected answer ids uuid\[], provided text text, is correct boolean, points awarded integer ); create table courseenrollments ( enrollment id bigserial primary key, user id uuid not null references users(user id) on delete cascade, course id uuid not null references courses(course id) on delete cascade, enrolled at timestamptz not null default current timestamp, status varchar(20) default 'notstarted' check (status in ('notstarted', 'inprogress', 'completed')), completed at timestamptz, unique (user id, course id) ); create table courseassignments ( assignment id uuid primary key default gen random uuid(), course id uuid not null references courses(course id) on delete cascade, assigned to user id uuid references users(user id) on delete cascade, assigned to role id uuid references roles(role id) on delete cascade, assigned to org id uuid references organizations(org id) on delete cascade, assigned to tag id uuid references tags(tag id) on delete cascade, assigning user id uuid references users(user id) on delete set null, assigned at timestamptz not null default current timestamp, due date date, is active boolean not null default true, check (assigned to user id is not null or assigned to role id is not null or assigned to org id is not null or assigned to tag id is not null) ); create table userlessonprogress ( progress id bigserial primary key, user id uuid not null references users(user id) on delete cascade, lesson id uuid not null references courselessons(lesson id) on delete cascade, course id uuid not null references courses(course id) on delete cascade, status varchar(20) default 'notstarted' check (status in ('notstarted', 'inprogress', 'completed', 'failedquiz')), started at timestamptz, completed at timestamptz, last quiz attempt id uuid references userquizattempts(attempt id) on delete set null, last accessed at timestamptz, unique (user id, lesson id) ); create table chatbotflows ( flow id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(100) not null unique, description text, flow definition jsonb not null, is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table chatchannels ( channel id uuid primary key default gen random uuid(), org id uuid references organizations(org id) on delete set null, channel type varchar(20) not null check (channel type in ('directmessage', 'ticket', 'device', 'general')), related ticket id bigint references tickets(ticket id) on delete set null, related device id uuid references devices(device id) on delete set null, name varchar(255), ai context summary text, is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz ); create table channelparticipants ( channel id uuid not null references chatchannels(channel id) on delete cascade, user id uuid not null references users(user id) on delete cascade, joined at timestamptz not null default current timestamp, last read message id bigint, fk is moderator boolean default false, primary key (channel id, user id) ); create table chatmessages ( message id bigserial primary key, channel id uuid not null references chatchannels(channel id) on delete cascade, user id uuid references users(user id) on delete set null, timestamp timestamptz not null default current timestamp, content type varchar(20) default 'text' check (content type in ('text', 'image', 'file', 'systemevent')), message body text, related attachment id uuid references attachments(attachment id) on delete set null, is edited boolean default false, edited at timestamptz, is deleted boolean default false, deleted at timestamptz ); \ add fk constraint now that chatmessages exists alter table channelparticipants add constraint fk participants last read foreign key (last read message id) references chatmessages(message id) on delete set null; create table pendingchatrequests ( pending request id uuid primary key default gen random uuid(), requestor user id uuid null references users(user id) on delete set null, if initiated by logged in portal user requestor contact id uuid null references contacts(contact id) on delete set null, if matched to a known contact requestor guest name varchar(100) null, name if provided by an unauthenticated visitor requestor guest email varchar(255) null, email if provided by an unauthenticated visitor target team id uuid null references teams(team id) on delete set null, target support team/queue based on entry point initial message text null, first message from the requestor status varchar(20) not null default 'pending' check (status in ('pending', 'accepted', 'abandoned', 'timeout')), created at timestamptz not null default current timestamp, accepted by user id uuid null references users(user id) on delete set null, tech who accepted accepted at timestamptz null, resulting channel id uuid null references chatchannels(channel id) on delete set null, channel created upon acceptance abandoned at timestamptz null if the user left before being connected \ add index on status, target team id, created at ); comment on table pendingchatrequests is 'tracks incoming live chat requests waiting for acceptance by a human agent '; comment on column pendingchatrequests target team id is 'the internal team designated to handle this type of chat request '; comment on column pendingchatrequests status is 'lifecycle status of the pending chat request '; create table surveys ( survey id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(255) not null unique, description text, trigger event varchar(50) not null check (trigger event in ('ticketresolved', 'ticketclosed', 'manual')), target audience varchar(20) default 'requester' check (target audience in ('requester', 'assignedtech', 'allcontacts')), delay after trigger interval, is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table surveyquestions ( question id uuid primary key default gen random uuid(), survey id uuid not null references surveys(survey id) on delete cascade, sequence order integer not null default 0, question text text not null, question type varchar(20) not null check (question type in ('ratingscale1 5', 'ratingscale1 10', 'nps', 'yesno', 'freetext', 'multiplechoicesingle')), is required boolean not null default true, allow comments boolean not null default false, options jsonb ); create table surveyinvitations ( invitation id uuid primary key default gen random uuid(), survey id uuid not null references surveys(survey id) on delete cascade, user id uuid references users(user id) on delete cascade, target email varchar(255), related ticket id bigint references tickets(ticket id) on delete set null, status varchar(20) default 'sent' check (status in ('sent', 'viewed', 'started', 'completed', 'expired')), sent at timestamptz not null default current timestamp, viewed at timestamptz, started at timestamptz, completed at timestamptz, token varchar(64) unique, expires at timestamptz ); create table surveyresponses ( response id bigserial primary key, invitation id uuid not null references surveyinvitations(invitation id) on delete cascade, question id uuid not null references surveyquestions(question id) on delete cascade, rating score integer, boolean answer boolean, text answer text, selected option id varchar(50), responded at timestamptz not null default current timestamp, unique(invitation id, question id) ); create table approvalworkflowdefinitions ( definition id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(255) not null unique, description text, approval logic jsonb not null, is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table approvalrequests ( approval request id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, definition id uuid references approvalworkflowdefinitions(definition id) on delete set null, requesting user id uuid not null references users(user id) on delete restrict, related entity type varchar(50) not null, related entity id varchar(50) not null, status varchar(20) default 'pending' check (status in ('pending', 'approved', 'rejected', 'cancelled')), decision summary text, completed at timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table approvalsteps ( approval step id bigserial primary key, approval request id uuid not null references approvalrequests(approval request id) on delete cascade, step order integer not null, step name varchar(100), approver user id uuid references users(user id) on delete set null, approver role id uuid references roles(role id) on delete set null, approval type varchar(20) default 'any' check (approval type in ('any', 'all')), status varchar(20) default 'pending' check (status in ('pending', 'approved', 'rejected', 'skipped')), decision made at timestamptz, due date timestamptz, check (approver user id is not null or approver role id is not null) ); create table approvalstepactions ( action id bigserial primary key, approval step id bigint not null references approvalsteps(approval step id) on delete cascade, acting user id uuid not null references users(user id) on delete restrict, action varchar(10) not null check (action in ('approve', 'reject', 'comment')), comment text, action timestamp timestamptz not null default current timestamp ); \ ============================================= \ identity & security monitoring \ ============================================= create table passwordsecurityfindings ( finding id bigserial primary key, org id uuid not null references organizations(org id) on delete cascade, device id uuid references devices(device id) on delete set null, account name varchar(255) not null, account type varchar(10) not null check (account type in ('local', 'domain')), domain name varchar(255), weakness type varchar(50) not null, details text, matched user id uuid references users(user id) on delete set null, first detected at timestamptz not null default current timestamp, last detected at timestamptz not null default current timestamp, status varchar(20) not null default 'active' check (status in ('active', 'remediated', 'acknowledged', 'ignored')), remediation ticket id bigint references tickets(ticket id) on delete set null, remediated at timestamptz, acknowledged at timestamptz, ignored until timestamptz \ unique constraint for active findings handled by index if needed ); create table darkwebbreachevents ( breach event id bigserial primary key, user email id bigint references useremails(user email id) on delete set null, email address varchar(255) not null, source breach name varchar(255), breach date date, discovered date date not null default current date, compromised data types text\[], password hash type varchar(50), password hash text, password plaintext available boolean not null default false, details url text, status varchar(20) not null default 'new' check (status in ('new', 'acknowledged', 'remediated', 'ignored')), acknowledged by user id uuid references users(user id) on delete set null, acknowledged at timestamptz, remediation notes text, first imported at timestamptz default current timestamp ); create table credentialtypes ( credential type id serial primary key, name varchar(100) not null unique, description text ); create table credentialfolders ( folder id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(100) not null, parent folder id uuid references credentialfolders(folder id) on delete cascade, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (org id, parent folder id, name) ); create table credentials ( credential id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, folder id uuid references credentialfolders(folder id) on delete set null, credential type id integer references credentialtypes(credential type id) on delete set null, name varchar(255) not null, username varchar(255), url text, notes text, expiry date date, external vault ref text not null unique, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table credentialpermissions ( permission id bigserial primary key, credential id uuid not null references credentials(credential id) on delete cascade, role id uuid references roles(role id) on delete cascade, user id uuid references users(user id) on delete cascade, permission level varchar(10) not null check (permission level in ('view', 'link')), granted at timestamptz not null default current timestamp, granted by user id uuid references users(user id) on delete set null, check (role id is not null or user id is not null), unique (credential id, role id, user id) ); create table credentiallinks ( credential id uuid not null references credentials(credential id) on delete cascade, target entity type varchar(50) not null, target entity id varchar(50) not null, linked at timestamptz not null default current timestamp, linked by user id uuid references users(user id) on delete set null, primary key (credential id, target entity type, target entity id) ); \ ============================================= \ agent & integration support \ ============================================= create table agentcommandqueue ( command queue id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, command type varchar(50) not null, script id uuid references scripts(script id) on delete set null, command parameters jsonb, status varchar(20) default 'pending' check (status in ('pending', 'senttoagent', 'inprogress', 'completed', 'failed', 'timeout', 'cancelled')), priority integer default 100, queued at timestamptz not null default current timestamp, sent at timestamptz, started at timestamptz, completed at timestamptz, result code integer, result output text, created by user id uuid references users(user id) on delete set null ); create table agentupdatepolicies ( update policy id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, msp org defining the policy name varchar(150) not null, description text null, target service version varchar(50) not null default 'latest stable', e g , 'latest stable', 'beta', '1 2 3' target ui version varchar(50) not null default 'latest stable', target probe version varchar(50) not null default 'latest stable', target updater version varchar(50) not null default 'latest stable', update schedule jsonb null, define update window, staggering (e g , { "window start local" "22 00", "window end local" "04 00", "days of week" \[0,1,2,3,4,5,6], "stagger minutes" 120 }) allow manual update boolean not null default true, allow manual trigger of updates outside schedule is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, unique (org id, name) ); comment on table agentupdatepolicies is 'defines policies for controlling agent component updates '; comment on column agentupdatepolicies target service version is 'desired version for the agent service component (can be specific or dynamic like ''latest stable'') '; comment on column agentupdatepolicies update schedule is 'jsonb defining the schedule and method for applying updates (e g , time windows, staggering) '; create table agentconfigurations ( agent config id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, msp org defining the configuration name varchar(150) not null, description text null, check in interval seconds integer not null default 300 check (check in interval seconds >= 60), log level varchar(10) not null default 'info' check (log level in ('debug', 'info', 'warning', 'error')), enabled features text\[] null, array of features enabled by this config (e g , \['monitoring', 'patching', 'remotecontrol', 'probenetworkscan']) update policy id uuid null references agentupdatepolicies(update policy id) on delete set null, link to the desired update policy probe config jsonb null, specific settings for the probe component (e g , { "scan threads" 4, "default snmp community" "public", "scan subnets" \["192 168 1 0/24"] }) is default for org boolean not null default false, is this the default config for new agents in the org? is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, unique (org id, name) ); comment on table agentconfigurations is 'defines configuration profiles for commandit agents '; comment on column agentconfigurations enabled features is 'array listing the features/modules activated by this configuration profile '; comment on column agentconfigurations probe config is 'jsonb containing specific configuration parameters for the network probe component '; create table deviceagentconfigassignments ( device id uuid primary key references devices(device id) on delete cascade, one config per device agent config id uuid not null references agentconfigurations(agent config id) on delete restrict, don't delete config if assigned assigned at timestamptz not null default current timestamp, assigned by user id uuid null references users(user id) on delete set null ); comment on table deviceagentconfigassignments is 'assigns an agent configuration profile to a specific device '; create table agentupdatestatuslog ( update log id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, component name varchar(50) not null check (component name in ('service', 'ui', 'probe', 'updater', 'agentbundle')), component being updated trigger source varchar(50) null, e g , 'scheduledpolicy', 'manualcommand', 'initialinstall' requested version varchar(50) not null, the version the update process aimed for current version varchar(50) null, version before the update attempt started status varchar(20) not null check (status in ('pending', 'downloading', 'installing', 'success', 'failed', 'cancelled', 'skipped')), attempt timestamp timestamptz not null default current timestamp, when the update process started completion timestamp timestamptz null, when the update process finished (successfully or not) error message text null, details if status is 'failed' details jsonb null additional details (e g , download size, exit code) ); comment on table agentupdatestatuslog is 'logs the status and outcome of agent component update attempts on devices '; comment on column agentupdatestatuslog component name is 'the specific agent component targeted by the update '; create table agentenrollmenttokens ( token hash varchar(64) primary key, hash of the enrollment token org id uuid not null references organizations(org id) on delete cascade, org this token is valid for description text null, optional description (e g , "token for new server deployments") expires at timestamptz not null, when the token becomes invalid max uses integer null, optional limit number of times token can be used (null for unlimited) current uses integer not null default 0, used by device ids uuid\[] null, optional track which devices used this token is active boolean not null default true, can this token currently be used? created at timestamptz not null default current timestamp, created by user id uuid null references users(user id) on delete set null ); comment on table agentenrollmenttokens is 'stores temporary tokens used for enrolling new commandit agents '; comment on column agentenrollmenttokens token hash is 'a secure hash of the enrollment token provided to the agent installer '; create table integrationinstances ( instance id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, integration type varchar(50) not null, name varchar(100) not null unique, configuration jsonb not null, is enabled boolean not null default true, status varchar(20) default 'ok' check (status in ('ok', 'error', 'disabled', 'syncing')), last sync time timestamptz, last error message text, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table aitools ( tool id uuid primary key default gen random uuid(), name varchar(100) not null unique, description text not null, parameters schema jsonb, implementation details jsonb, is active boolean not null default true ); create table aiagentconfigs ( config id uuid primary key default gen random uuid(), name varchar(100) not null unique, description text, system prompt text not null, llm model name varchar(100) not null, model parameters jsonb, is platform defined boolean not null default false ); create table aiagentconfigtools ( config id uuid not null references aiagentconfigs(config id) on delete cascade, tool id uuid not null references aitools(tool id) on delete cascade, primary key (config id, tool id) ); create table spaiagentoverrides ( override id uuid primary key default gen random uuid(), sp org id uuid not null references organizations(org id) on delete cascade, base config id uuid not null references aiagentconfigs(config id) on delete restrict, display name varchar(100), avatar url text, prompt additions text, is active boolean not null default true, unique(sp org id, base config id) ); create table magiclogintokens ( token hash varchar(64) primary key, user id uuid not null references users(user id) on delete cascade, expires at timestamptz not null, created at timestamptz not null default current timestamp, used at timestamptz ); create table ssotokens ( token hash varchar(64) primary key, user id uuid not null references users(user id) on delete cascade, expires at timestamptz not null, created at timestamptz not null default current timestamp, used at timestamptz, intended url text ); \ ============================================= \ ancillary / specific modules \ ============================================= create table riskregister ( risk id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, risk identifier varchar(50) not null unique, title varchar(255) not null, description text, category varchar(100), owner user id uuid references users(user id) on delete set null, likelihood varchar(20), impact varchar(20), risk score integer, status varchar(20) default 'open' check (status in ('open', 'mitigating', 'monitoring', 'closed', 'accepted')), treatment plan text, last assessed at date, next review date date, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table riskcontrols ( risk id uuid not null references riskregister(risk id) on delete cascade, control id uuid not null references complianceframeworkcontrols(control id) on delete cascade, primary key (risk id, control id) ); create table vendorcontracts ( contract id uuid primary key default gen random uuid(), vendor org id uuid not null references organizations(org id) on delete cascade, managing org id uuid not null references organizations(org id) on delete cascade, contract name varchar(255) not null, contract number varchar(100), start date date not null, end date date, renewal type varchar(20) check (renewal type in ('autorenew', 'manual', 'none')), notice period days integer, status varchar(20) default 'active' check (status in ('draft', 'active', 'expired', 'terminated')), value numeric(19,4), currency code varchar(3), notes text, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table vendorcontractattachments ( contract id uuid not null references vendorcontracts(contract id) on delete cascade, attachment id uuid not null references attachments(attachment id) on delete cascade, description text, attached at timestamptz not null default current timestamp, primary key (contract id, attachment id) ); create table vendorriskassessments ( assessment id uuid primary key default gen random uuid(), vendor org id uuid not null references organizations(org id) on delete cascade, managing org id uuid not null references organizations(org id) on delete cascade, assessment date date not null default current date, assessor user id uuid references users(user id) on delete set null, risk score integer, summary text, recommendation text, next assessment date date, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table bcdrplans ( plan id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, name varchar(255) not null unique, description text, plan type varchar(50) check (plan type in ('bcp', 'drp', 'incidentresponse')), owner user id uuid references users(user id) on delete set null, status varchar(20) default 'draft' check (status in ('draft', 'active', 'archived')), document id uuid references documents(document id) on delete set null, recovery time objective seconds integer, recovery point objective seconds integer, last reviewed at date, next review date date, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table bcdrplanassets ( bcdr plan asset id bigserial primary key, plan id uuid not null references bcdrplans(plan id) on delete cascade, target entity type varchar(50) not null, e g , 'device', 'application', 'location' target entity id varchar(50) not null, uuid or other id criticality varchar(20) check (criticality in ('veryhigh', 'high', 'medium', 'low')), recovery strategy varchar(30) null check (recovery strategy in ('hotstandby', 'warmstandby', 'coldrestorebackup', 'replicateandrestore', 'rebuildmanual', 'other')), required backup policy id uuid null references backuppolicies(policy id) on delete set null, link to expected backup policy recovery notes text, specific recovery steps/notes for this asset created at timestamptz not null default current timestamp, updated at timestamptz, unique (plan id, target entity type, target entity id) ); comment on column bcdrplanassets recovery strategy is 'defines the planned recovery method for this asset (e g , hotstandby, coldrestorebackup) '; comment on column bcdrplanassets required backup policy id is 'optional link to the backuppolicy expected to meet the rpo requirements for this critical asset '; create table bcdrtests ( test id uuid primary key default gen random uuid(), plan id uuid not null references bcdrplans(plan id) on delete cascade, test date date not null, test type varchar(50), scenario text, outcome varchar(20) check (outcome in ('success', 'partialsuccess', 'failure')), summary text, duration seconds integer, lead tester user id uuid references users(user id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table bcdrtestparticipants ( test id uuid not null references bcdrtests(test id) on delete cascade, user id uuid not null references users(user id) on delete cascade, role in test varchar(100), primary key (test id, user id) ); create table projects ( project id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, client org project is for managing org id uuid not null references organizations(org id) on delete cascade, msp org managing the project project number varchar(50) not null unique, name varchar(255) not null, description text, status varchar(20) default 'planning' check (status in ('planning', 'active', 'onhold', 'completed', 'cancelled')), project manager user id uuid references users(user id) on delete set null, start date date, end date date, estimated budget numeric(19,4), actual cost numeric(19,4), currency code varchar(3) null, currency for budget/cost fields (e g , 'cad', 'usd') estimated hours numeric(10,2), actual hours numeric(10,2), inbound email address varchar(255) unique null, unique email address for sending updates directly to this project created at timestamptz not null default current timestamp, updated at timestamptz auto updated by trigger ); comment on column projects inbound email address is 'unique email address ({guid}@commandit net) for sending updates directly to this project '; create table projectphases ( phase id uuid primary key default gen random uuid(), project id uuid not null references projects(project id) on delete cascade, name varchar(100) not null, sequence order integer not null default 0, start date date, end date date, status varchar(20) default 'notstarted' check (status in ('notstarted', 'inprogress', 'completed', 'skipped')), unique (project id, name) ); create table projecttasks ( project task id bigserial primary key, project id uuid not null references projects(project id) on delete cascade, phase id uuid references projectphases(phase id) on delete set null, name varchar(255) not null, description text, status varchar(20) default 'notstarted' check (status in ('notstarted', 'inprogress', 'completed', 'onhold', 'cancelled')), assigned user id uuid references users(user id) on delete set null, work type id uuid references worktypes(work type id) on delete set null, estimated hours numeric(10,2), actual hours numeric(10,2) default 0 00, start date date, due date date, completed at timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table projecttaskdependencies ( task id bigint not null references projecttasks(project task id) on delete cascade, predecessor task id bigint not null references projecttasks(project task id) on delete cascade, dependency type varchar(10) default 'fs' check (dependency type in ('fs', 'ss', 'ff', 'sf')), lag days integer default 0, primary key (task id, predecessor task id), check (task id != predecessor task id) ); create table portalbrandingsettings ( org id uuid primary key references organizations(org id) on delete cascade, primary color varchar(7), secondary color varchar(7), logo override url text, favicon url text, portal title varchar(100), updated at timestamptz auto updated ); \ stores notes/email logs for projects create table projectupdates ( project update id bigserial primary key, project id uuid not null references projects(project id) on delete cascade, user id uuid references users(user id) on delete set null, user or ai creating the update timestamp timestamptz not null default current timestamp, body text not null, note type varchar(30) null check (note type in ('standard', 'internal', 'emaillog', 'systemevent')), created at timestamptz not null default current timestamp ); create index idx projectupdates project id on projectupdates(project id, timestamp desc); comment on table projectupdates is 'stores notes, logs of emails received via project email address, and other updates related to a project '; \ links attachments to project updates create table projectupdateattachments ( project update attachment id bigserial primary key, project update id bigint not null references projectupdates(project update id) on delete cascade, attachment id uuid not null references attachments(attachment id) on delete cascade, added at timestamptz not null default current timestamp, unique (project update id, attachment id) ); comment on table projectupdateattachments is 'links file attachments (stored in attachments table) to specific projectupdates '; \ ============================================= \ automations \ ============================================= create table automations ( automation id uuid primary key default gen random uuid(), owner org id uuid references organizations(org id) on delete cascade, scope type varchar(20) not null default 'organization' check (scope type in ('global', 'organization')), scope id uuid null, name varchar(255) not null, description text, automation type varchar(20) not null check (automation type in ('script', 'workflow')), script id uuid references scripts(script id) on delete set null, workflow definition jsonb, target os types text\[], e g , \['windows'], \['linux', 'macos'], null/\['any'] target cpu architectures text\[], e g , \['x86 64'], \['arm64'], null/\['any'] input parameters schema jsonb, output variables jsonb, execution timeout seconds integer default 300, is system defined boolean not null default false, is available adhoc boolean not null default true, is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint automations owner scope name unique unique nulls not distinct (owner org id, scope type, scope id, name) ); create table automationschedules ( automation schedule id uuid primary key default gen random uuid(), definer org id uuid not null references organizations(org id) on delete cascade, name varchar(255) not null, description text, is active boolean not null default true, automation id uuid not null references automations(automation id) on delete restrict, input variable values jsonb, approval request id uuid null references approvalrequests(approval request id) on delete set null, \ targeting definition target scope type varchar(20) check (target scope type in ('organization', 'location', 'device', 'taggroup')), target scope id uuid null, \ scheduling definition schedule type varchar(20) not null check (schedule type in ('onetime', 'hourly', 'daily', 'weekly', 'monthly')), one time run at utc timestamptz null, interval integer default 1 check (interval >= 1), days of week integer\[], months of year integer\[], monthly occurrence type varchar(30) check (monthly occurrence type in ('specificday', 'ordinaldayofmonth', 'lastday', 'weekdayofmonth', 'weekenddayofmonth')), monthly day of month integer, monthly ordinal integer, monthly day specifier varchar(20), scheduled time local time not null, defines time for non hourly, start for hourly? needs clarification in logic minute offset integer null check (minute offset >= 0 and minute offset < 60), target timezone mode varchar(20) not null default 'devicelocal' check (target timezone mode in ('devicelocal', 'specifictimezone')), specific timezone varchar(100), \ execution options override execution timeout seconds integer null, set downtime during execution boolean not null default false, missed execution behavior varchar(10) not null default 'runasap' check (missed execution behavior in ('runasap', 'skip')), run asap within seconds integer null, \ execution tracking calculated next run utc timestamptz null, last run start time utc timestamptz null, error message text, \ timestamps created at timestamptz not null default current timestamp, updated at timestamptz, auto updated \ constraints unique(definer org id, name), check ( (target scope type != 'taggroup' and target scope id is not null) or (target scope type = 'taggroup' and target scope id is null) ), check ( (schedule type = 'onetime' and one time run at utc is not null) or (schedule type != 'onetime' and one time run at utc is null) ) ); create table automationscheduletagtargets ( m2m for tag targeting automation schedule id uuid not null references automationschedules(automation schedule id) on delete cascade, tag id uuid not null references tags(tag id) on delete cascade, primary key (automation schedule id, tag id) ); create table automationscheduleexecutions ( tracks individual device runs triggered by a schedule execution id bigserial primary key, automation schedule id uuid not null references automationschedules(automation schedule id) on delete cascade, target device id uuid not null references devices(device id) on delete cascade, scheduled run time utc timestamptz not null, calculated utc time for this specific device run command queue id bigint references agentcommandqueue(command queue id) on delete set null, link to the actual command execution status varchar(20) not null default 'pending' check (status in ('pending', 'queued', 'running', 'completed', 'failed', 'skippedtimezone')), start time utc timestamptz null, when agentcommandqueue entry started end time utc timestamptz null, when agentcommandqueue entry finished result summary text, brief result or error message created at timestamptz not null default current timestamp when this execution record was planned ); create table automationcomponents ( component id uuid primary key default gen random uuid(), name varchar(100) not null unique, user facing name (e g , "run script") category id integer null references automationcomponentcategories(category id) on delete set null, link to category description text, help pane detailed description/tooltip icon varchar(100), optional ui icon component type varchar(20) not null check (component type in ('script', 'workflow', 'agentaction', 'apicall', 'controlflow', 'aitool')), implementation ref varchar(255), reference based on component type input schema jsonb, help pane defines input parameters (name, type, description, required, example) using json schema output schema jsonb, help pane defines output parameters (name, type, description) using json schema or array minimum agent version varchar(30) null, help pane minimum agent version required (e g , "1 2 0") is system defined boolean not null default true, is enabled boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); create table automationcomponentcategories ( category id serial primary key, name varchar(100) not null unique, name of the category (e g , "control flow", "filesystem") parent category id integer null references automationcomponentcategories(category id) on delete cascade, for hierarchical categories description text null, optional description sort order integer not null default 0, controls display order in the ui tree created at timestamptz not null default current timestamp, updated at timestamptz, auto updated constraint category cannot be own parent check (category id != parent category id) \ note if names only need to be unique within a parent, adjust the unique constraint \ unique (parent category id, name) requires careful handling of null parent category id ); create table userfavoriteautomationcomponents ( user id uuid not null references users(user id) on delete cascade, user who favorited component id uuid not null references automationcomponents(component id) on delete cascade, component that was favorited added at timestamptz not null default current timestamp, when it was favorited primary key (user id, component id) ensures a user can only favorite a component once ); create table notes ( note id bigserial primary key, target entity type varchar(50) not null, e g , 'organization', 'location', 'user', 'device', 'ticket', 'peripheral', 'agreement', 'softwareproduct', 'wirelessnetwork', 'equipmentasset' target entity id varchar(50) not null, stores uuid or bigint/int as string based on entity type note type varchar(30) null check (note type in ('description', 'comment', 'update', 'alert', 'systemlog', 'flaggednote')), consider refining this enum based on actual use note content text not null, the content of the note is pinned boolean not null default false, renamed from is flagged if true, indicates note should be displayed prominently (pinned) created at timestamptz not null default current timestamp, created by user id uuid references users(user id) on delete set null, updated at timestamptz, auto updated by trigger updated by user id uuid references users(user id) on delete set null ); \ comments comment on table notes is 'stores notes associated with various entities (devices, tickets, orgs, etc ) using polymorphic association '; comment on column notes target entity type is 'the type of entity this note is linked to (e g , ''device'', ''ticket'') '; comment on column notes target entity id is 'the primary key (uuid, id) of the entity this note is linked to '; comment on column notes is pinned is 'if true, indicates this note should be displayed prominently or pinned at the top in ui views '; \ indexes create index idx notes target on notes(target entity type, target entity id); create index idx notes pinned target on notes(target entity type, target entity id, is pinned, created at desc); index to efficiently retrieve pinned notes first create table portalnotices ( portal notice id uuid primary key default gen random uuid(), creator org id uuid not null references organizations(org id) on delete cascade, org creating/managing the notice title varchar(255) not null, content text not null, content to display (supports markdown/html?) notice level varchar(20) default 'info' check (notice level in ('info', 'warning', 'critical', 'maintenance')), for ui styling display start time timestamptz not null default current timestamp, when notice becomes visible display end time timestamptz null, optional when notice automatically hides (null = indefinite) \ targeting scope scope type varchar(20) not null check (scope type in ('globaltoclients', 'organization', 'location')), scope entity id uuid null, stores target orgid or locationid based on scope type null for globaltoclients target role ids uuid\[] null, array of role ids that can see this notice null/empty means all roles within scope \ status & timestamps is active boolean not null default true, master enabled/disabled toggle created by user id uuid references users(user id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated by trigger \ constraints check ( (scope type = 'globaltoclients' and scope entity id is null) or (scope type = 'organization' and scope entity id is not null) or (scope type = 'location' and scope entity id is not null) ) ); \ ============================================= \ active directory object synchronization \ ============================================= create table addomains ( ad domain id uuid primary key default gen random uuid(), internal commandit id org id uuid not null references organizations(org id) on delete cascade, owning org domain name varchar(255) not null unique, fully qualified domain name (e g , ad example com) netbios name varchar(15) unique, domain sid varchar(100) unique, security identifier for the domain functional level varchar(50), e g , 'windows2016server' monitored by probe device id uuid references devices(device id) on delete set null, which probe/agent monitors this domain \ fsmo role holders (links to adcomputers which should represent dcs) fsmo schema master dc id uuid null references adcomputers(ad computer id) on delete set null, fsmo domain naming master dc id uuid null references adcomputers(ad computer id) on delete set null, fsmo pdc emulator dc id uuid null references adcomputers(ad computer id) on delete set null, fsmo rid master dc id uuid null references adcomputers(ad computer id) on delete set null, fsmo infrastructure master dc id uuid null references adcomputers(ad computer id) on delete set null, last sync time timestamptz, is active boolean not null default true, is monitoring enabled? created at timestamptz not null default current timestamp, updated at timestamptz ); comment on table addomains is 'represents discovered active directory domains and their core properties, including fsmo role holders '; comment on column addomains fsmo schema master dc id is 'link to the adcomputers record for the dc holding the schema master role '; comment on column addomains fsmo domain naming master dc id is 'link to the adcomputers record for the dc holding the domain naming master role '; comment on column addomains fsmo pdc emulator dc id is 'link to the adcomputers record for the dc holding the pdc emulator role '; comment on column addomains fsmo rid master dc id is 'link to the adcomputers record for the dc holding the rid master role '; comment on column addomains fsmo infrastructure master dc id is 'link to the adcomputers record for the dc holding the infrastructure master role '; create table adorganizationalunits ( ad ou id uuid primary key default gen random uuid(), internal commandit id ad domain id uuid not null references addomains(ad domain id) on delete cascade, object guid uuid unique, ad guid (if available and reliable) distinguished name text not null unique, full dn (e g , ou=sales,dc=ad,dc=example,dc=com) name varchar(255) not null, name of the ou itself parent ou id uuid references adorganizationalunits(ad ou id) on delete cascade, self reference for hierarchy gpo link order jsonb, optional store gpo link order if needed is protected boolean default false, accidental deletion protection flag in ad last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz ); create table adusers ( ad user id uuid primary key default gen random uuid(), internal commandit id ad domain id uuid not null references addomains(ad domain id) on delete cascade, object guid uuid unique, ad guid object sid varchar(100) unique, ad sid sam account name varchar(256) not null, user principal name varchar(255), distinguished name text not null unique, display name varchar(255), first name varchar(100), last name varchar(100), email address varchar(255), description text, is enabled boolean, is locked out boolean, last logon timestamp timestamptz, last bad password time timestamptz, bad password count integer, password last set timestamptz, password expires timestamptz, password never expires boolean, cannot change password boolean, smart card required boolean, when created timestamptz, timestamp from ad when changed timestamptz, timestamp from ad commandit user id uuid references users(user id) on delete set null, optional link to matched commandit user last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (ad domain id, sam account name) ); create table adgroups ( ad group id uuid primary key default gen random uuid(), internal commandit id ad domain id uuid not null references addomains(ad domain id) on delete cascade, object guid uuid unique, object sid varchar(100) unique, sam account name varchar(256) not null, distinguished name text not null unique, group scope varchar(20), 'domainlocal', 'global', 'universal' group type varchar(20), 'security', 'distribution' description text, email address varchar(255), when created timestamptz, when changed timestamptz, last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (ad domain id, sam account name) ); create table adcomputers ( ad computer id uuid primary key default gen random uuid(), internal commandit id ad domain id uuid not null references addomains(ad domain id) on delete cascade, object guid uuid unique, object sid varchar(100) unique, sam account name varchar(256) not null, usually hostname$ dns hostname varchar(255), distinguished name text not null unique, operating system varchar(255), operating system version varchar(50), description text, is enabled boolean, last logon timestamp timestamptz, password last set timestamptz, when created timestamptz, when changed timestamptz, commandit device id uuid references devices(device id) on delete set null, optional link to matched commandit device last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (ad domain id, sam account name) ); create table adgroupmemberships ( membership id bigserial primary key, ad group id uuid not null references adgroups(ad group id) on delete cascade, the group member type varchar(10) not null check (member type in ('user', 'group', 'computer')), type of member member object guid uuid not null, guid of the member (user, group, or computer) member object sid varchar(100), sid of the member (denormalized for convenience) member dn text, dn of the member (denormalized for convenience) first seen timestamptz not null default current timestamp, last seen timestamptz not null default current timestamp, is active boolean not null default true, track if membership is currently seen unique (ad group id, member object guid) a member can only be in a group once \ add index on member object guid for reverse lookups ("what groups is x a member of?") ); create table adgrouppolicyobjects ( ad gpo id uuid primary key default gen random uuid(), internal commandit id ad domain id uuid not null references addomains(ad domain id) on delete cascade, object guid uuid unique, gpo guid display name varchar(255) not null, gpo status varchar(20), 'enabled', 'disabled', 'usersettingsdisabled', 'computersettingsdisabled' description text, when created timestamptz, when changed timestamptz, \ consider storing version numbers if detailed change tracking is needed \ consider storing a hash or summary of key settings if policy drift detection is needed last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (ad domain id, display name) ); create table adgpolinks ( gpo link id bigserial primary key, ad gpo id uuid not null references adgrouppolicyobjects(ad gpo id) on delete cascade, target type varchar(10) not null check (target type in ('domain', 'ou')), target domain id uuid references addomains(ad domain id) on delete cascade, target ou id uuid references adorganizationalunits(ad ou id) on delete cascade, is enforced boolean, link order integer, is enabled boolean, is the link itself enabled? last sync time timestamptz, check ((target type = 'domain' and target domain id is not null and target ou id is null) or (target type = 'ou' and target domain id is null and target ou id is not null)), unique (ad gpo id, target domain id) where target domain id is not null, unique (ad gpo id, target ou id) where target ou id is not null ); \ ============================================= \ ad change & security event logging \ ============================================= create table adobjectchangelog ( change log id bigserial primary key, ad domain id uuid not null references addomains(ad domain id) on delete cascade, change timestamp timestamptz not null default current timestamp, when change was detected/recorded source dc varchar(255), domain controller where change originated (if known) object guid uuid, guid of the object changed object sid varchar(100), object dn text, object type varchar(20), 'user', 'group', 'computer', 'gpo', 'ou', 'groupmembership' change type varchar(20) not null check (change type in ('create', 'modify', 'delete', 'addmember', 'removemember', 'linkgpo', 'unlinkgpo')), attribute name varchar(255), for 'modify' type old value text, for 'modify' type new value text, for 'modify', 'addmember', 'removemember' (stores member dn/sid) actor user sid varchar(100), sid of user/principal performing the action (if available from logs) raw event data jsonb optional storage for raw event details ); create table deviceeventlogs ( device event log id bigserial primary key, renamed pk event timestamp utc timestamptz not null, timestamp from the source event log (utc preferred) recorded at timestamptz not null default current timestamp, when commandit recorded it reporting device id uuid references devices(device id) on delete set null, endpoint/probe reporting event org id uuid null references organizations(org id) on delete cascade, org context (derived from device) event definition id uuid null references eventlogdefinitions(definition id) on delete set null, link to predefined event if matched source log name varchar(100), e g , 'security', 'system', 'application', 'auth log' channel path text null, specific windows event channel path if applicable source name varchar(255) null, specific event source name if applicable event id varchar(50) not null, event id (numeric/string) source event record id varchar(255) null, unique identifier from the source os log (e g , windows event record id) used for server side deduplication level varchar(20), 'information', 'warning', 'error', 'auditsuccess', 'auditfailure' etc event type varchar(50) null check (event type in ('loginsuccess', 'loginfailure', 'accountlockout', 'passwordchange', 'passwordreset', 'groupmembershipchange', 'privilegeuse', 'other')), categorization ad domain id uuid references addomains(ad domain id) on delete set null, context target user name varchar(255) null, target user sid varchar(100) null, target device name varchar(255) null, target device sid varchar(100) null, source ip address inet null, source port integer null, source hostname varchar(255) null, logon type integer null, details text null, event message/description dynamic parameters jsonb null, extracted parameters raw event data jsonb null full raw event data \ consider partitioning this table by timestamp for performance ); \ comments comment on table deviceeventlogs is 'stores event log entries collected from managed devices or other integrated sources '; comment on column deviceeventlogs org id is 'organization context, typically derived from the reporting device '; comment on column deviceeventlogs event definition id is 'link to the eventlogdefinitions table if this event was reported based on a known definition '; comment on column deviceeventlogs channel path is 'specific windows event channel path, if applicable '; comment on column deviceeventlogs source name is 'specific event source name (e g , microsoft windows security auditing) '; comment on column deviceeventlogs source event record id is 'unique identifier for the event from the source os log (e g , windows event record id) used for server side deduplication '; comment on column deviceeventlogs details is 'generic description (if using definition lookup) or specific message from the event payload '; comment on column deviceeventlogs dynamic parameters is 'stores key dynamic values extracted from the event message payload, especially when using definition lookup '; \ add relevant indexes (with updated names) create index idx deviceeventlogs timestamp on deviceeventlogs(event timestamp utc desc); create index idx deviceeventlogs device on deviceeventlogs(reporting device id) where reporting device id is not null; create index idx deviceeventlogs org time on deviceeventlogs(org id, event timestamp utc desc) where org id is not null; create index idx deviceeventlogs event id on deviceeventlogs(event id); create index idx deviceeventlogs dedupe on deviceeventlogs (reporting device id, source event record id) where reporting device id is not null and source event record id is not null; create table addomainsecuritypolicy ( ad domain id uuid primary key references addomains(ad domain id) on delete cascade, links 1 to 1 with the domain \ password policy settings min password length integer, max password age days integer, max lifetime in days (0 if disabled) min password age days integer, min lifetime in days password history count integer, number of passwords remembered password complexity enabled boolean, enforce complexity requirements? reversible encryption enabled boolean, store passwords using reversible encryption? (highly discouraged) source password policy gpo id uuid references adgrouppolicyobjects(ad gpo id) on delete set null, optional gpo guid that defined these settings \ account lockout policy settings lockout threshold integer, number of failed attempts before lockout (0 if disabled) lockout duration minutes integer, duration of lockout in minutes (0 for manual unlock) reset lockout counter after minutes integer, time after which failed attempt counter resets source lockout policy gpo id uuid references adgrouppolicyobjects(ad gpo id) on delete set null, optional gpo guid that defined these settings \ kerberos policy settings (common examples) max ticket age hours integer, maximum lifetime for user ticket max renew age days integer, maximum lifetime for user ticket renewal max service ticket age minutes integer, maximum lifetime for service ticket max clock skew minutes integer, maximum tolerance for computer clock synchronization enforce user logon restrictions boolean, validate user rights and account restrictions for service tickets? source kerberos policy gpo id uuid references adgrouppolicyobjects(ad gpo id) on delete set null, optional gpo guid that defined these settings \ sync info last sync time timestamptz, when these effective settings were last retrieved/updated updated at timestamptz auto updated by trigger (when commandit record changes) ); \ ============================================= \ azure ad object synchronization \ ============================================= create table azureadusers ( azure ad user id uuid primary key default gen random uuid(), internal commandit id integration instance id uuid not null references integrationinstances(instance id) on delete cascade, link to the specific azure tenant integration azure object id uuid not null, azure ad user object id (guid) user principal name varchar(255) not null, upn (e g , user\@domain com) display name varchar(255), account enabled boolean, is the account enabled in azure ad? mfa methods registered text\[], list of registered mfa methods (e g , 'phoneappotp', 'sms', 'fido2') mfa capable boolean, calculated based on methods or specific property is guest boolean, is this a b2b guest user? creation type varchar(50), e g , 'cloud', 'synced' created date time timestamptz, timestamp from azure ad last sign in date time timestamptz null, last successful interactive sign in sign in risk level varchar(20), requires azure ad premium p1/p2 ('low', 'medium', 'high', 'none') commandit user id uuid references users(user id) on delete set null, optional link to matched commandit user last sync time timestamptz, when commandit last synced this record created at timestamptz not null default current timestamp, updated at timestamptz, unique (integration instance id, azure object id), unique (integration instance id, user principal name) \ add index on azure object id, user principal name ); create table azureadgroups ( azure ad group id uuid primary key default gen random uuid(), internal commandit id integration instance id uuid not null references integrationinstances(instance id) on delete cascade, azure object id uuid not null, azure ad group object id (guid) display name varchar(255) not null, description text, group types text\[], e g , \['unified', 'security'] or \['security'] membership rule text, rule for dynamic membership (if applicable) is assignable to role boolean, can azure ad roles be assigned to this group? visibility varchar(50), e g , 'public', 'private' (for m365 groups) mail enabled boolean, security enabled boolean, created date time timestamptz, last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (integration instance id, azure object id), unique (integration instance id, display name) assuming display names are unique per tenant scope in practice \ add index on azure object id ); create table azureadgroupmemberships ( membership id bigserial primary key, internal commandit id integration instance id uuid not null references integrationinstances(instance id) on delete cascade, group object id uuid not null, azure guid of the group member type varchar(20) not null check (member type in ('user', 'group', 'device', 'serviceprincipal')), member object id uuid not null, azure guid of the member member display name varchar(255), denormalized member name for easier display first seen timestamptz not null default current timestamp, when commandit first saw this membership last seen timestamptz not null default current timestamp, when commandit last confirmed this membership is active boolean not null default true, track if membership is currently seen vs historical unique (integration instance id, group object id, member object id) \ add index on group object id, member object id ); \ ============================================= \ azure ad policy and configuration sync \ ============================================= create table azureadconditionalaccesspolicies ( azure ad ca policy id uuid primary key default gen random uuid(), internal commandit id integration instance id uuid not null references integrationinstances(instance id) on delete cascade, azure policy id varchar(50) not null, azure's policy guid/id display name varchar(255) not null, state varchar(50) not null, 'enabled', 'disabled', 'enabledforreportingbutnotenforced' conditions jsonb, { users, applications, locations, platforms, devicestates, signinrisklevels } grant controls jsonb, { operator ('and'/'or'), builtincontrols ('mfa', 'compliantdevice', etc ), customcontrols, termsofuse } session controls jsonb, { applicationenforcedrestrictions, cloudappsecurity, signinfrequency, persistentbrowsersession } last modified date time timestamptz, created date time timestamptz, last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (integration instance id, azure policy id) \ add index on azure policy id ); create table azureadtenantsecuritypolicy ( integration instance id uuid primary key references integrationinstances(instance id) on delete cascade, links 1 to 1 with the azure tenant integration \ security defaults & mfa security defaults enabled boolean, mfa enforcement method varchar(50), 'securitydefaults', 'conditionalaccess', 'peruser', 'none' \ password policy (cloud or synced) password policy settings jsonb, { minlength, complexityrequired, lockoutthreshold, lockoutduration } password protection enabled boolean, azure ad password protection for on prem sync \ self service password reset (sspr) sspr enabled scope varchar(20), 'all', 'selected', 'none' sspr target group ids uuid\[], azure group object ids if scope=selected sspr auth methods required integer, number of methods needed for reset sspr allowed auth methods text\[], \['email', 'mobilephone', 'officephone', 'securityquestions', 'appnotification', 'appcode'] \ external identities / collaboration b2b external users allowed boolean, guest invite restrictions varchar(255), guest user access level varchar(50), 'sameasmembers', 'limitedaccess', 'restrictedaccess' \ device management device registration allowed scope varchar(20), 'all', 'selected', 'none' require mfa to join devices boolean, require device marked as compliant boolean, via intune/mdm for ca require hybrid azure ad joined boolean, via intune/mdm for ca \ other settings \ (add other relevant tenant wide security settings as needed) last sync time timestamptz, updated at timestamptz auto updated by trigger ); \ ============================================= \ azure ad application and consent tracking \ ============================================= create table azureadapplications ( represents application registrations azure ad app id uuid primary key default gen random uuid(), internal commandit id integration instance id uuid not null references integrationinstances(instance id) on delete cascade, azure app id varchar(50) not null, application (client) id display name varchar(255), publisher domain varchar(255), sign in audience varchar(100), e g , 'azureadmyorg', 'azureadmultipleorgs', 'azureadandpersonalmicrosoftaccount' created date time timestamptz, last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (integration instance id, azure app id) \ add index on azure app id ); create table azureadappserviceprincipals ( represents enterprise applications / service principals azure ad sp id uuid primary key default gen random uuid(), internal commandit id integration instance id uuid not null references integrationinstances(instance id) on delete cascade, azure object id uuid not null, service principal object id app id varchar(50) not null, application (client) id it represents display name varchar(255), service principal type varchar(50), e g , 'application', 'managedidentity' account enabled boolean, app owner org id uuid, azure tenant id of the app publisher homepage url text, logout url text, reply urls text\[], notes text, tags text\[], last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (integration instance id, azure object id), unique (integration instance id, app id) typically 1 sp per app id in a tenant \ add index on azure object id, app id ); create table azureadappoauth2permissionsgrants ( tracks delegated permissions (user/admin consent) grant id varchar(255) primary key, the unique id of the grant object from azure ad graph api integration instance id uuid not null references integrationinstances(instance id) on delete cascade, consent type varchar(50), 'allprincipals' (admin consent) or 'principal' (user consent) principal id uuid null, user/group object id if consent type='principal' (who consented/granted on behalf of) client id uuid not null, service principal object id of the client application receiving the permission resource id uuid not null, service principal object id of the resource application (api) being accessed scope text not null, space delimited string of granted permission scopes (e g , 'user read mail read') grant time timestamptz, when the grant was initially recorded by azure expiry time timestamptz, when the grant expires (less common for oauth2) last sync time timestamptz \ add index on client id, resource id, principal id ); create table azureadapproleassignments ( tracks assignment of users/groups to application roles assignment id varchar(255) primary key, the unique id of the assignment object from azure ad graph api integration instance id uuid not null references integrationinstances(instance id) on delete cascade, app role id uuid not null, id of the specific app role (defined on the resource app) being assigned principal id uuid not null, object id of the user, group, or service principal being assigned the role principal type varchar(50), 'user', 'group', 'serviceprincipal' resource id uuid not null, object id of the service principal of the resource application (whose role is being assigned) resource display name varchar(255), denormalized resource sp name principal display name varchar(255), denormalized principal name created date time timestamptz, when assignment was made last sync time timestamptz \ add index on principal id, resource id ); \ ============================================= \ azure ad event logging (selective) \ ============================================= create table azureadriskysigninevents ( store flagged sign ins from identity protection event id varchar(255) primary key, id from azure ad audit/signin logs or identity protection event integration instance id uuid not null references integrationinstances(instance id) on delete cascade, correlation id varchar(50), request id varchar(50), user principal name varchar(255), user display name varchar(255), user object id uuid, risk level aggregated varchar(20), 'low', 'medium', 'high' (at time of event) risk level during signin varchar(20), risk state varchar(20), 'atrisk', 'confirmedcompromised', 'remediated', 'dismissed' risk detail varchar(100), e g , 'anonymizedipaddress', 'unfamiliarlocation', 'malwareinfectedip' risk event types text\[], types of detections contributing ip address inet, location jsonb, { city, state, countryorregion, geocoordinates { latitude, longitude } } device detail jsonb, { deviceid, displayname, operatingsystem, browser, iscompliant, ismanaged } authentication details jsonb, { authmethod ('password', 'mfa', 'federated'), succeeded } conditional access status varchar(50), 'success', 'failure', 'notapplied', 'reportonlysuccess' etc mfa result varchar(50), result of mfa challenge if performed application display name varchar(255), application id varchar(50), client app id resource display name varchar(255), resource id varchar(50), resource app id created date time timestamptz not null, event time utc recorded at timestamptz not null default current timestamp, when commandit recorded it status varchar(20) default 'new' check (status in ('new', 'investigating', 'remediated', 'dismissed', 'falsepositive')) \ add index on created date time, user principal name, risk level aggregated, status ); \ ============================================= \ azure ad privileged identity management (pim) \ ============================================= create table azureadpimroleassignments ( tracks eligible and active pim role assignments assignment id varchar(255) primary key, id of the assignment schedule instance from graph api integration instance id uuid not null references integrationinstances(instance id) on delete cascade, role definition id varchar(255) not null, id of the azure ad role definition (e g , global admin) role display name varchar(255), denormalized role name principal id uuid not null, object id of the user or group assigned principal type varchar(20), 'user', 'group' directory scope id varchar(255) not null, usually the tenant id ('/') or an administrative unit id assignment state varchar(20) not null check (assignment state in ('eligible', 'active')), type of assignment assignment type varchar(20), 'activated', 'assigned' start date time timestamptz, when the assignment/activation starts end date time timestamptz, when the assignment/activation ends (can be permanent) justification text, justification provided during activation ticket number varchar(50), associated ticket number if provided during activation activated by user id uuid, user who activated an eligible assignment last sync time timestamptz \ add index on principal id, role definition id, assignment state ); \ ============================================= \ sql server monitoring tables \ ============================================= create table sqlserverinstances ( sql instance id uuid primary key default gen random uuid(), internal commandit id host device id uuid not null references devices(device id) on delete cascade, device hosting the instance org id uuid not null references organizations(org id) on delete cascade, inherited from host device instance name varchar(255) not null, e g , mssqlserver (default), sqlexpress, or named instance version varchar(100), e g , '15 0 2000 5' (sql server 2019 rtm) edition varchar(100), e g , 'standard edition', 'enterprise edition', 'express' product level varchar(50), e g , 'rtm', 'sp1', 'cu10' service name varchar(255), windows service name service account varchar(255), account running the sql server service authentication mode varchar(50), 'windows authentication', 'mixed mode' tcp port integer, is clustered boolean, status varchar(50), 'running', 'stopped', 'unknown' collation varchar(100), max server memory mb integer, min server memory mb integer, processors used integer, \ add other key instance level configuration settings as needed last assessment time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (host device id, instance name) ); create table sqldatabases ( sql database id uuid primary key default gen random uuid(), internal commandit id sql instance id uuid not null references sqlserverinstances(sql instance id) on delete cascade, database name varchar(255) not null, status varchar(50), 'online', 'offline', 'restoring', 'recovering', 'suspect' size mb bigint, recovery model varchar(20), 'simple', 'full', 'bulk logged' collation varchar(100), owner sid varchar(100), owner name varchar(255), compatibility level integer, e g , 150 (sql 2019), 140 (sql 2017) created date timestamptz, is read only boolean, auto shrink enabled boolean, \ add other key database level settings as needed (e g , encryption, file paths) last assessment time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (sql instance id, database name) ); create table sqllogins ( sql login id uuid primary key default gen random uuid(), internal commandit id sql instance id uuid not null references sqlserverinstances(sql instance id) on delete cascade, name varchar(255) not null, sid varchar(100), sid if windows login/group type varchar(20) not null check (type in ('sqllogin', 'windowsuser', 'windowsgroup', 'certificate', 'asymmetrickey')), default database varchar(255), is disabled boolean, password policy enforced boolean, for sqllogin password expiration enabled boolean, for sqllogin last login time timestamptz, server roles text\[], e g , \['sysadmin', 'serveradmin'] database permissions jsonb, store simplified db user mappings/roles if needed { "dbname" \["db owner", "db datareader"] } created date timestamptz, last modified date timestamptz, last assessment time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (sql instance id, name) ); create table sqlagentjobs ( sql agent job id uuid primary key default gen random uuid(), internal commandit id sql instance id uuid not null references sqlserverinstances(sql instance id) on delete cascade, job name varchar(255) not null, category name varchar(100), owner sid varchar(100), owner name varchar(255), is enabled boolean, last run outcome varchar(50), 'succeeded', 'failed', 'cancelled', 'unknown' last run timestamp timestamptz, last run duration seconds integer, next run timestamp timestamptz, schedule details jsonb, store schedule info (frequency, time, etc ) job steps summary jsonb, optional store number of steps, types last assessment time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (sql instance id, job name) ); \ ============================================= \ exchange monitoring tables \ ============================================= create table exchangeservers ( primarily for on premises exchange exchange server id uuid primary key default gen random uuid(), internal commandit id host device id uuid not null references devices(device id) on delete cascade, org id uuid not null references organizations(org id) on delete cascade, inherited from host device exchange version varchar(100), e g , 'version 15 2 (build 986 5)' (exchange 2019 cu12) edition varchar(50), 'standard', 'enterprise' installed roles text\[], \['mailbox', 'edgetransport', 'clientaccess'] ad site name varchar(255), last assessment time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz ); create table exchangedatabases ( primarily for on premises exchange exchange database id uuid primary key default gen random uuid(), internal commandit id exchange server id uuid not null references exchangeservers(exchange server id) on delete cascade, database name varchar(255) not null, status varchar(50), 'mounted', 'dismounted', 'unknown' size gb bigint, circular logging enabled boolean, last full backup timestamptz, \ add other key database settings as needed (e g , file paths, mailbox count) last assessment time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (exchange server id, database name) ); create table exchangemailboxes ( can represent on prem or exchange online mailboxes exchange mailbox id uuid primary key default gen random uuid(), internal commandit id org id uuid not null references organizations(org id) on delete cascade, owning org integration instance id uuid references integrationinstances(instance id) on delete set null, fk if exchange online via integration exchange server id uuid references exchangeservers(exchange server id) on delete set null, fk if on prem mailbox azure ad user id uuid references azureadusers(azure ad user id) on delete set null, optional link to azure ad user ad user id uuid references adusers(ad user id) on delete set null, optional link to ad user display name varchar(255) not null, primary smtp address varchar(255) not null, user principal name varchar(255), mailbox guid uuid, mailbox type varchar(30) not null check (mailbox type in ('usermailbox', 'sharedmailbox', 'roommailbox', 'equipmentmailbox', 'linkedmailbox', 'discoverymailbox')), size mb bigint, item count bigint, prohibit send quota mb bigint, issue warning quota mb bigint, archive status varchar(20), 'none', 'active', 'autoexpanding' archive size mb bigint, litigation hold enabled boolean, litigation hold duration days integer, retention policy name varchar(255), forwarding address varchar(255), email address if forwarding is set deliver to mailbox and forward boolean, last logon time timestamptz, is active sync enabled boolean, is owa enabled boolean, \ add other relevant settings (e g , permissions summary, specific protocol enablement) last assessment time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, check ((integration instance id is not null and exchange server id is null) or (integration instance id is null and exchange server id is not null)), must be exo or onprem, not both/neither directly linked here unique (org id, primary smtp address) assuming unique within an org context ); create table exchangeconnectors ( primarily for on premises exchange send/receive connectors exchange connector id uuid primary key default gen random uuid(), internal commandit id exchange server id uuid not null references exchangeservers(exchange server id) on delete cascade, connector name varchar(255) not null, connector type varchar(20) not null check (connector type in ('send', 'receive')), is enabled boolean, usage varchar(50), for receive connectors (e g , 'client', 'internal', 'partner') bindings jsonb, ip/port bindings for receive connectors address spaces text\[], for send connectors (domains it handles) smart hosts text\[], for send connectors source transport servers text\[], for send connectors security settings jsonb, authentication methods, tls settings last assessment time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (exchange server id, connector name) ); \ ============================================= \ vulnerability scan results tables \ ============================================= \ revised based on user request for org/location/ci level linking create table externalscantargets ( scan target id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, org responsible for this target definition target varchar(255) not null, ip address, hostname, or cidr range description text, \ context links (nullable fks to allow different levels of association) location id uuid null references locations(location id) on delete set null, optional link to a specific location device id uuid null references devices(device id) on delete set null, optional link if this target maps to a specific managed device (ci) is active boolean default true, is this target definition currently active for scanning? created at timestamptz not null default current timestamp, updated at timestamptz, \ ensure the target itself is unique within the responsible org unique (org id, target) \ we might add a check constraint if we want to enforce rules like "if device id is set, location id must match device's location", but keeping it simple for now ); comment on table externalscantargets is 'defines external assets (ips, hostnames, cidrs) to be monitored or scanned, allowing association at the organization, location, or specific device (ci) level '; comment on column externalscantargets org id is 'the organization primarily responsible for or associated with this external target '; comment on column externalscantargets target is 'the actual ip address, hostname, or cidr range representing the external asset '; comment on column externalscantargets location id is 'optional fk to locations, linking this external target to a specific site '; comment on column externalscantargets device id is 'optional fk to devices, linking this external target directly to a managed configuration item (e g , a firewall, external web server represented as a device) '; create table vulnerabilityscans ( scan id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, org context for the scan scan type varchar(10) not null check (scan type in ('internal', 'external')), type of scan run scan engine varchar(100), e g , 'nmap', 'openvas', 'qualyscloudplatform', 'internalscript', 'agentscan' scan name varchar(255), user defined name for the scan job/policy scan start time timestamptz not null, scan end time timestamptz, status varchar(20) not null check (status in ('pending', 'running', 'completed', 'failed', 'cancelled')), targets scanned text\[], list of targets included in this specific scan run (ips, hostnames, cidrs) source probe device id uuid null references devices(device id) on delete set null, probe device used for internal scans scan configuration jsonb, details about scan settings used (e g , policy name, intensity) initiated by user id uuid references users(user id) on delete set null, created at timestamptz not null default current timestamp ); comment on table vulnerabilityscans is 'stores metadata about executed vulnerability scans, both internal and external '; comment on column vulnerabilityscans scan type is 'indicates whether the scan was run against internal network assets or external targets '; comment on column vulnerabilityscans targets scanned is 'list of specific ips, hostnames, or cidrs included in this particular scan execution '; comment on column vulnerabilityscans source probe device id is 'the probe device that executed this scan, typically used for internal scans '; comment on column vulnerabilityscans scan configuration is 'jsonb field storing details about the scan settings, policy used, intensity, etc '; \ ============================================= \ unified vulnerability findings table (revised) \ ============================================= create table vulnerabilityfindings ( finding id bigserial primary key, unique id for this finding instance org id uuid not null references organizations(org id) on delete cascade, org context \ context nullable fields depending on finding source type device id uuid null references devices(device id) on delete cascade, link to device if internal finding or if external target is matched scan id uuid null references vulnerabilityscans(scan id) on delete set null, link to the specific scan job that found this (internal or external) scan target varchar(255) null, target ip/hostname from external scan \ vulnerability identification vulnerability id varchar(100) not null, cve, check id, vuln name key, etc use a consistent scheme vulnerability name text, detection source varchar(100) not null, e g , 'externalscantoolx', 'agentcompliancecheck', 'agentpatchscan', 'manualentry', 'internalscantooly' finding source type varchar(10) not null check (finding source type in ('internal', 'external')), discriminator \ vulnerability details (general not nessus specific) severity varchar(20) check (severity in ('info', 'low', 'medium', 'high', 'critical')), cvss base score numeric(3, 1) null, cvss temporal score numeric(3, 1) null, cvss vector string varchar(255) null, cross references jsonb null, store additional ids like bid, osvdb, etc e g , {"bid" \["12345"]} exploit available boolean null, exploit frameworks text\[] null, e g , \['metasploit', 'exploitdb'] reference urls text\[] null, general reference urls ('see also') \ finding context port integer null, port if applicable protocol varchar(10) null check (protocol in ('tcp', 'udp', 'icmp')), protocol if applicable service name varchar(100) null, service if applicable software product id uuid null references softwareproducts(software product id) on delete set null, link if related to specific software \ finding details & remediation description text, description of the vulnerability/finding evidence text, output or proof from the scanner/check remediation text, recommended fix or link to kb \ status & tracking status varchar(20) default 'new' check (status in ('new', 'investigating', 'confirmed', 'riskaccepted', 'remediated', 'falsepositive')), first seen time timestamptz not null default current timestamp, when this finding first appeared for this target last seen time timestamptz not null default current timestamp, when this finding was last confirmed present by a scan/check status updated time timestamptz, when the status field was last changed status updated by user id uuid null references users(user id) on delete set null, related ticket id bigint null references tickets(ticket id) on delete set null, optional link to remediation ticket notes text, internal notes about this finding \ uniqueness constraint needs careful thought this attempts uniqueness based on key identifiers per target type and source unique (device id, vulnerability id, port, protocol, detection source) where finding source type = 'internal' and device id is not null, unique (scan target, vulnerability id, port, protocol, scan id) where finding source type = 'external' and scan target is not null and scan id is not null ); comment on table vulnerabilityfindings is 'unified table storing vulnerability findings from both internal (device based) and external scans/sources '; comment on column vulnerabilityfindings finding source type is 'indicates if the finding originated from an internal source (like an agent on a device) or an external scan '; comment on column vulnerabilityfindings device id is 'link to the specific device if the finding is internal or if an external target was successfully mapped to a managed device '; comment on column vulnerabilityfindings scan id is 'link to the specific scan job record (vulnerabilityscans table) if the finding came from a defined scan '; comment on column vulnerabilityfindings scan target is 'the ip address or hostname targeted by an external scan where the finding was observed '; comment on column vulnerabilityfindings vulnerability id is 'primary identifier for the vulnerability (e g , cve id, internal check id) '; comment on column vulnerabilityfindings cross references is 'jsonb storing related vulnerability identifiers (e g , bid, mskb) '; comment on column vulnerabilityfindings cvss vector string is 'the full cvss vector string (v2 or v3) for detailed scoring context '; comment on column vulnerabilityfindings exploit available is 'boolean indicating if a known, public exploit is readily available '; comment on column vulnerabilityfindings exploit frameworks is 'array listing frameworks known to contain exploits for this vulnerability '; comment on column vulnerabilityfindings reference urls is 'array of urls providing further information about the vulnerability (e g , vendor advisories, cve details) '; \ add relevant indexes create index idx vulnfindings device on vulnerabilityfindings(device id) where device id is not null; create index idx vulnfindings scan target on vulnerabilityfindings(scan target) where scan target is not null; create index idx vulnfindings scan id on vulnerabilityfindings(scan id) where scan id is not null; create index idx vulnfindings vuln id on vulnerabilityfindings(vulnerability id); create index idx vulnfindings status on vulnerabilityfindings(status); create index idx vulnfindings severity on vulnerabilityfindings(severity); create index idx vulnfindings org status on vulnerabilityfindings(org id, status); create index idx vulnfindings source type on vulnerabilityfindings(finding source type); \ ============================================= \ network share discovery tables \ ============================================= create table networkshares ( discovered shares on devices network share id uuid primary key default gen random uuid(), internal commandit id host device id uuid not null references devices(device id) on delete cascade, org id uuid not null references organizations(org id) on delete cascade, inherited from host device share name varchar(255) not null, e g , 'c$', 'sharedocs', 'printers' path text not null, local path on the host device being shared description text, share type varchar(20) check (share type in ('disk', 'printer', 'ipc', 'special', 'unknown')), based on windows share types max users integer, maximum concurrent users allowed ( 1 for unlimited) last seen time timestamptz, when the agent last confirmed this share exists created at timestamptz not null default current timestamp, updated at timestamptz, unique (host device id, share name) ); create table sharepermissions ( share level permissions (acls) share permission id bigserial primary key, internal commandit id network share id uuid not null references networkshares(network share id) on delete cascade, account name varchar(255) not null, user or group name (e g , 'domain\user', 'builtin\administrators', 'everyone') account sid varchar(100), optional sid of the user/group for better accuracy access type varchar(10) not null check (access type in ('allow', 'deny')), permission varchar(20) not null check (permission in ('read', 'change', 'fullcontrol')), last seen time timestamptz, unique (network share id, account name, permission, access type) should be unique combination ); \ ============================================= \ backup monitoring \ ============================================= create table backupjobstatus ( backup job status id bigserial primary key, org id uuid not null references organizations(org id) on delete cascade, org context device id uuid not null references devices(device id) on delete cascade, device being backed up integration instance id uuid null references integrationinstances(instance id) on delete set null, link to backup system integration config job name varchar(255) not null, name of the backup job job type varchar(50), e g , 'filesystem', 'sql', 'exchange', 'vm', 'full', 'incremental' last run timestamp timestamptz null, when the job last attempted to run (or completed) last run status varchar(30) check (last run status in ('success', 'warning', 'failed', 'running', 'cancelled', 'skipped', 'unknown')), last success timestamp timestamptz null, timestamp of the last successful completion next run timestamp timestamptz null, estimated next scheduled run time (if available) backup set details text, brief description of what is included (e g , "c \users", "full system") destination text, description of backup destination (e g , "cloud storage", "local nas") last run size bytes bigint, size of data backed up in last run last run duration seconds integer, duration of last run last error message text, error message if last run status was 'failed' last reported at timestamptz not null default current timestamp, when commandit last received this status update created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, job name) assuming job names are unique per device \ add index on last run status, last success timestamp ); create table backupjobconfiguration ( backup job config id bigserial primary key, org id uuid not null references organizations(org id) on delete cascade, device id uuid not null references devices(device id) on delete cascade, integration instance id uuid null references integrationinstances(instance id) on delete set null, job name varchar(255) not null, backup system job id varchar(255) null, \ common configuration settings is enabled boolean, backup method varchar(50), schedule summary text, schedule details jsonb, source summary text, source details jsonb, retention summary text, retention details jsonb, encryption enabled boolean, encryption type varchar(50), application aware processing boolean, consistency check enabled boolean, \ sync info last config sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, job name) ); create table backuppolicies ( policy id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, msp defining the policy name varchar(150) not null, description text, is default for org boolean not null default false, org's default backup policy is active boolean not null default true, \ schedule expectation expected frequency varchar(20) not null check (expected frequency in ('hourly', 'daily', 'weekly', 'monthly', 'workdays', 'weekends', 'manualonly')), how often should a success be seen? alert if missed after hours integer not null default 25, grace period (hours) after expected run time before alerting on 'missed' \ result handling rules (defines action taken based on reported status) \ action options 'createticket', 'alertonly', 'createticketandalert', 'ignore' on success action varchar(30) not null default 'ignore', on success ticket template id uuid references tickettemplates(template id) on delete set null, on warning action varchar(30) not null default 'alertonly', on warning ticket template id uuid references tickettemplates(template id) on delete set null, on failure action varchar(30) not null default 'createticketandalert', on failure ticket template id uuid references tickettemplates(template id) on delete set null, on missed action varchar(30) not null default 'createticketandalert', action if alert if missed after hours breached on missed ticket template id uuid references tickettemplates(template id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (org id, name) ); create table backupdestinations ( backup destination id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, org managing this destination definition name varchar(255) not null, user friendly name (e g , "primary nas office", "azure blob vault westus", "rotating usb set alpha") media type varchar(30) not null check (media type in ('disk', 'nas', 'san', 'tapelibrary', 'tapedrive', 'cloudstorage', 'usb', 'networkpath', 'other')), location description text null, free text description (e g , "azure west us region", "basement rack 3", "fireproof safe room 101") physical location id uuid null references locations(location id) on delete set null, link if it corresponds to a managed location is offsite boolean not null default false, is this considered offsite relative to primary data? is immutable boolean not null default false, does the destination support/enforce immutability? rotation type varchar(20) null check (rotation type in ('none', 'mediaperbackup', 'daily', 'weekly', 'monthly', 'gfs')), media rotation strategy if applicable access details jsonb null, store non sensitive connection info (e g , { "provider" "azureblob", "container" "prod backups", "url" " " } ) sensitive parts use credentials table credential id uuid null references credentials(credential id) on delete set null, link to stored credentials if needed for access created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (org id, name) ); comment on table backupdestinations is 'defines reusable backup storage destinations and their properties '; comment on column backupdestinations is offsite is 'indicates if this storage location is considered physically separate (offsite) '; comment on column backupdestinations is immutable is 'indicates if the storage destination supports object/backup immutability '; comment on column backupdestinations rotation type is 'describes the media rotation strategy, if applicable (especially for disk/tape/usb) '; comment on column backupdestinations access details is 'stores non sensitive connection details (urls, paths, container names) use credential id for secrets '; create table backupjobdestinationlinks ( backup job config id bigint not null references backupjobconfiguration(backup job config id) on delete cascade, backup destination id uuid not null references backupdestinations(backup destination id) on delete cascade, role varchar(20) not null check (role in ('primary', 'secondary', 'archive')), role of this destination for the job (primary target, copy/replication target, long term archive) last verified connectivity timestamptz null, optional track when connectivity was last confirmed primary key (backup job config id, backup destination id, role) job can link to same dest maybe with diff role? ); comment on table backupjobdestinationlinks is 'links backup job configurations to their backup destinations, specifying the role (primary, secondary, archive) '; comment on column backupjobdestinationlinks role is 'the role this destination plays for the linked backup job '; create table tagbackuppolicyassignments ( assignment id bigserial primary key, tag id uuid not null references tags(tag id) on delete cascade, tag assigned to device or org backup policy id uuid not null references backuppolicies(policy id) on delete cascade, is enabled boolean not null default true, priority integer not null default 0, conflict resolution priority assigned at timestamptz not null default current timestamp, unique (tag id, backup policy id) ); \ ============================================= \ website & domain monitoring tables \ ============================================= create table monitoredwebsites ( website id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, client org this site belongs to url text not null, url to monitor (e g , https //www example com) display name varchar(255), user friendly name check interval seconds integer not null default 300, how often to check expected status code integer default 200, expected http status code for 'up' expected content match text null, optional string that must be present in response body check ssl expiry boolean not null default true, check ssl certificate validity? ssl expiry threshold days integer default 30, days before expiry to warn ssl expiry date date null, last known ssl cert expiry date ssl issuer varchar(255) null, last known ssl cert issuer last check time timestamptz null, when the last check was performed last status varchar(30) default 'unknown' check (last status in ('up', 'down', 'error', 'contentmismatch', 'sslexpiringsoon', 'sslexpired', 'unknown')), last response time ms integer null, response time in milliseconds last error message text null, is active boolean not null default true, is monitoring enabled? created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (org id, url) ); create table monitoreddomains ( domain id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, client org this domain belongs to org domain id bigint null references organizationdomains(org domain id) on delete set null, link to the corresponding verified domain association domain name varchar(255) not null, registrar name varchar(255) null, name of the domain registrar registration expiry date date null, domain registration expiry expiry threshold days integer default 30, days before expiry to warn dns check config jsonb null, define dns records to check { "checks" \[ {"type" "a", "host" "@", "expected value" "1 2 3 4"}, {"type" "mx", "expected value" "mail example com ", "preference" 10} ]} last check time timestamptz null, last status varchar(30) default 'unknown' check (last status in ('ok', 'expiringsoon', 'expired', 'dnsmismatch', 'error', 'unknown')), last error message text null, is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (org id, domain name) ); comment on table monitoreddomains is 'tracks domains being actively monitored for registration expiry and dns records '; comment on column monitoreddomains org domain id is 'link to the corresponding verified domain association in organizationdomains table '; create index idx monitoreddomains org domain id on monitoreddomains(org domain id); \ ============================================= \ cloud infrastructure inventory \ ============================================= create table cloudsubscriptions ( cloud subscription id uuid primary key default gen random uuid(), internal commandit id integration instance id uuid not null references integrationinstances(instance id) on delete cascade, link to azure tenant integration org id uuid not null references organizations(org id) on delete cascade, associated client org azure subscription id varchar(50) not null, azure subscription guid display name varchar(255) not null, state varchar(50), e g , 'enabled', 'warned', 'pastdue', 'disabled' tenant id varchar(50), azure tenant guid \ cost center varchar(100), if available via tags/management groups last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (integration instance id, azure subscription id) ); create table cloudresourcegroups ( cloud resource group id uuid primary key default gen random uuid(), internal commandit id cloud subscription id uuid not null references cloudsubscriptions(cloud subscription id) on delete cascade, azure rg name varchar(255) not null, name of the azure resource group azure rg id text unique, full azure resource id location varchar(100), azure region tags jsonb, resource tags from azure last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (cloud subscription id, azure rg name) ); \ note cloud virtual machines are primarily handled by enhancing the 'devices' table \ set devices device type = 'cloudserver' \ store cloud specific details in devices configuration jsonb \ { "cloud provider" "azure", \ "azure vm id" "/subscriptions/ /resourcegroups/ /providers/microsoft compute/virtualmachines/ ", \ "azure resource group name" " ", \ "azure subscription id" " ", \ "vm size" "standard d2s v3", \ "region" "canadacentral", \ "power state" "vm running", \ "private ip addresses" \["10 0 0 4"], \ "public ip addresses" \["20 x x x"] \ } create table cloudmanageddatabases ( cloud database id uuid primary key default gen random uuid(), internal commandit id cloud subscription id uuid not null references cloudsubscriptions(cloud subscription id) on delete cascade, cloud resource group id uuid references cloudresourcegroups(cloud resource group id) on delete set null, azure db resource id text unique, full azure resource id name varchar(255) not null, db type varchar(50) not null, e g , 'azuresqldatabase', 'azuresqlmanagedinstance', 'azuremysql', 'azurepostgresql' server name varchar(255), name of logical server if applicable location varchar(100), sku jsonb, { "name" "gp gen5", "tier" "generalpurpose", "capacity" 2 } status varchar(50), e g , 'online', 'creating', 'disabled' collation varchar(100), tags jsonb, last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz ); create table cloudstorageaccounts ( cloud storage account id uuid primary key default gen random uuid(), internal commandit id cloud subscription id uuid not null references cloudsubscriptions(cloud subscription id) on delete cascade, cloud resource group id uuid references cloudresourcegroups(cloud resource group id) on delete set null, azure sa resource id text unique, full azure resource id name varchar(255) not null, location varchar(100), account kind varchar(50), e g , 'storagev2', 'blobstorage' sku jsonb, { "name" "standard lrs", "tier" "standard" } access tier varchar(50), 'hot', 'cool' (for blob) https only enabled boolean, tls version varchar(10), minimum tls version required tags jsonb, last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz ); create table cloudsubscriptioncosts ( aggregated costs per subscription period subscription cost id bigserial primary key, cloud subscription id uuid not null references cloudsubscriptions(cloud subscription id) on delete cascade, billing period start date date not null, billing period end date date not null, currency code varchar(3) not null, total cost numeric(19, 4) not null, cost by service jsonb, optional breakdown like { "virtual machines" 123 45, "storage" 67 89 } cost by resource group jsonb, optional breakdown { "rg prod" 100 00, "rg dev" 91 34 } last updated timestamptz not null default current timestamp, when this cost data was retrieved/calculated unique (cloud subscription id, billing period start date) ); \ aws create table awsaccounts ( aws account id uuid primary key default gen random uuid(), internal commandit id integration instance id uuid not null references integrationinstances(instance id) on delete cascade, link to aws integration config org id uuid not null references organizations(org id) on delete cascade, associated client org aws account number varchar(20) not null unique, aws account number (12 digits) account alias varchar(100) null, friendly alias for the account iam user alias varchar(100) null, alias for iam sign in url root email varchar(255) null, email of the root user (if known, use with caution) status varchar(50) default 'active', 'active', 'suspended', 'closed' \ consider adding ou info if using aws organizations heavily last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz ); comment on table awsaccounts is 'represents linked aws accounts '; create table awsvpcs ( aws vpc record id uuid primary key default gen random uuid(), internal commandit id aws account id uuid not null references awsaccounts(aws account id) on delete cascade, aws vpc id varchar(50) not null unique, aws vpc id (e g , vpc xxxxxxxx) region varchar(50) not null, aws region (e g , us east 1) cidr block cidr not null, is default boolean, state varchar(20), 'pending', 'available' tags jsonb, last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz ); comment on table awsvpcs is 'represents aws virtual private clouds (vpcs) '; create table awsrdsinstances ( includes databases, not just servers aws rds instance id uuid primary key default gen random uuid(), internal commandit id aws account id uuid not null references awsaccounts(aws account id) on delete cascade, db instance identifier varchar(255) not null, user defined identifier db instance arn text unique, full aws arn region varchar(50) not null, engine varchar(50), 'mysql', 'postgres', 'sqlserver se', 'oracle ee', etc engine version varchar(50), db instance class varchar(50), e g , 'db t3 micro' instance state varchar(50), 'available', 'creating', 'modifying', 'deleting' endpoint address varchar(255), endpoint port integer, allocated storage gb integer, storage type varchar(50), 'gp2', 'io1', etc multi az boolean, publicly accessible boolean, tags jsonb, last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (aws account id, region, db instance identifier) ); comment on table awsrdsinstances is 'represents aws relational database service (rds) instances '; create table awss3buckets ( aws s3 bucket id uuid primary key default gen random uuid(), internal commandit id aws account id uuid not null references awsaccounts(aws account id) on delete cascade, bucket name varchar(255) not null unique, globally unique bucket name region varchar(50), region where the bucket resides creation date timestamptz, public access block configuration jsonb, versioning enabled boolean, logging target bucket varchar(255), logging target prefix varchar(255), tags jsonb, last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz ); comment on table awss3buckets is 'represents aws simple storage service (s3) buckets '; create table awsaccountcosts ( account cost id bigserial primary key, aws account id uuid not null references awsaccounts(aws account id) on delete cascade, billing period start date date not null, billing period end date date not null, currency code char(3) not null, removed fk to currencycodes for flexibility total cost numeric(19, 4) not null, cost by service jsonb, optional breakdown like { "amazonec2" 123 45, "amazons3" 67 89 } cost by region jsonb, optional breakdown { "us east 1" 100 00, "ca central 1" 91 34 } last updated timestamptz not null default current timestamp, unique (aws account id, billing period start date) ); comment on table awsaccountcosts is 'aggregated aws costs per account billing period '; \ gcp create table gcpprojects ( gcp project record id uuid primary key default gen random uuid(), internal commandit id integration instance id uuid not null references integrationinstances(instance id) on delete cascade, link to gcp integration config org id uuid not null references organizations(org id) on delete cascade, associated client org gcp project id varchar(100) not null unique, gcp project id (e g , 'my cool project 123') gcp project number bigint unique, gcp project number name varchar(100), friendly name parent type varchar(20), 'organization', 'folder' parent id varchar(50), id of the parent org or folder lifecycle state varchar(20), 'active', 'delete requested' labels jsonb, gcp labels (similar to tags) last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz ); comment on table gcpprojects is 'represents linked google cloud platform (gcp) projects '; create table gcpvpcnetworks ( gcp vpc record id uuid primary key default gen random uuid(), internal commandit id gcp project record id uuid not null references gcpprojects(gcp project record id) on delete cascade, gcp network id bigint unique, gcp network resource id name varchar(255) not null, self link text unique, full gcp resource url auto create subnetworks boolean, routing mode varchar(20), 'regional', 'global' description text, last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique(gcp project record id, name) ); comment on table gcpvpcnetworks is 'represents gcp virtual private cloud (vpc) networks '; create table gcpcloudsqlinstances ( gcp cloudsql instance id uuid primary key default gen random uuid(), internal commandit id gcp project record id uuid not null references gcpprojects(gcp project record id) on delete cascade, instance name varchar(255) not null, user defined instance name self link text unique, full gcp resource url region varchar(50) not null, database version varchar(50), e g , 'mysql 8 0', 'postgres 14' state varchar(50), 'runnable', 'suspended', 'pending create' settings tier varchar(50), e g , 'db f1 micro' settings data disk size gb bigint, settings ip configuration jsonb, includes authorized networks, private ip etc settings backup configuration jsonb, settings availability type varchar(20), 'regional', 'zonal' labels jsonb, last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (gcp project record id, instance name) ); comment on table gcpcloudsqlinstances is 'represents gcp cloud sql instances '; create table gcpcloudstoragebuckets ( gcp cs bucket id uuid primary key default gen random uuid(), internal commandit id gcp project record id uuid not null references gcpprojects(gcp project record id) on delete cascade, bucket name varchar(255) not null unique, globally unique bucket name self link text unique, full gcp resource url location varchar(50), e g , 'us central1' location type varchar(20), 'region', 'multi region', 'dual region' storage class varchar(50), 'standard', 'nearline', 'coldline', 'archive' versioning enabled boolean, logging config jsonb, iam configuration jsonb, lifecycle rules jsonb, labels jsonb, last sync time timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz ); comment on table gcpcloudstoragebuckets is 'represents gcp cloud storage buckets '; create table gcpprojectcosts ( project cost id bigserial primary key, gcp project record id uuid not null references gcpprojects(gcp project record id) on delete cascade, billing period start date date not null, billing period end date date not null, currency code char(3) not null, removed fk to currencycodes for flexibility total cost numeric(19, 4) not null, cost by service jsonb, optional breakdown like { "compute engine" 123 45, "cloud storage" 67 89 } cost by sku jsonb, optional more granular breakdown last updated timestamptz not null default current timestamp, unique (gcp project record id, billing period start date) ); comment on table gcpprojectcosts is 'aggregated gcp costs per project billing period '; \ update devices table comment for cloud vms comment on column devices configuration is 'jsonb field storing device specific configuration examples cloud vm details (provider, vm id, size, region, power state, ips), pbx details (type, provider, admin url), network device details (snmp oid) '; \ example json for aws ec2 in devices configuration \ { "cloud provider" "aws", \ "aws instance id" "i 0abcdef1234567890", \ "aws account number" "123456789012", \ "aws vpc id" "vpc abcdef", \ "aws subnet id" "subnet abcdef", \ "instance type" "t3 micro", \ "region" "us east 1", \ "availability zone" "us east 1a", \ "power state" "running", \ "private ip address" "172 31 10 5", \ "public ip address" "54 x x x", \ "ami id" "ami 0abcdef123" \ } \ example json for gcp gce in devices configuration \ { "cloud provider" "gcp", \ "gcp instance id" "1234567890123456789", \ "gcp project id" "my cool project 123", \ "gcp zone" "us central1 a", \ "machine type" "e2 micro", \ "power status" "running", \ "network interfaces" \[ { "network" "global/networks/default", "networkip" "10 128 0 2", "accessconfigs" \[ { "natip" "35 x x x" } ] } ], \ "disk info" \[ { "devicename" "boot", "disksizegb" 10 } ] \ } \ ============================================= \ telecom & network connectivity \ ============================================= create table telecomcarriers ( carrier id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, msp managing the carrier info name varchar(255) not null unique, e g , 'telus', 'shaw', 'twilio', 'bell' account number varchar(100), support phone varchar(50), support email varchar(255), notes text, created at timestamptz not null default current timestamp, updated at timestamptz ); \ phone systems can be modeled using the devices table \ set devices device type = 'pbx' or 'voipsystem' \ store details in devices configuration jsonb \ { "system type" "cloudpbx", "provider name" "ringcentral", "admin url" " ", "version" " " } \ or \ { "system type" "onprempbx", "internal ip" " ", "version" " ", "make" "avaya", "model" "ip office" } create table phonenumbers ( tracking dids, toll free, main lines etc phone number id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, client org owning/using the number e164 number varchar(30) not null unique, phone number in e 164 format (e g , +16045551234) number type varchar(20) not null check (number type in ('did', 'tollfree', 'main', 'fax', 'service', 'mobile')), carrier id uuid null references telecomcarriers(carrier id) on delete set null, assigned to type varchar(20) null check (assigned to type in ('user', 'device', 'location', 'callqueue', 'autoattendant')), assigned to entity id varchar(50) null, uuid or other id based on assigned to type is sms enabled boolean default false, can this number send/receive carrier sms? notes text, created at timestamptz not null default current timestamp, updated at timestamptz ); create table phoneextensions ( extension id bigserial primary key, org id uuid not null references organizations(org id) on delete cascade, client org extension number varchar(20) not null, phone system device id uuid not null references devices(device id) on delete cascade, link to the pbx/voipsystem device record assigned user id uuid null references users(user id) on delete set null, user assigned this extension assigned device id uuid null references devices(device id) on delete set null, optional link to physical handset device record voicemail enabled boolean, display name varchar(100), caller id name associated created at timestamptz not null default current timestamp, updated at timestamptz, unique (phone system device id, extension number) ); create table telecomcircuits ( circuit id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, client org using the circuit carrier id uuid not null references telecomcarriers(carrier id) on delete restrict, circuit identifier varchar(100) not null, carrier's circuit id or unique identifier circuit type varchar(30) not null check (circuit type in ('internet fibre', 'internet coax', 'internet dsl', 'mpls', 'p2p fibre', 'p2p ethernet', 'wan', 'sip trunk', 'prit1', 'prie1', 'pots', 'other')), status varchar(20) not null default 'active' check (status in ('planning', 'provisioning', 'active', 'inactive', 'decommissioned')), bandwidth mbps down numeric(10, 2) null, bandwidth mbps up numeric(10, 2) null, endpoint a location id uuid null references locations(location id) on delete set null, location a (e g , office) endpoint a details text null, specific demarcation details for a endpoint b location id uuid null references locations(location id) on delete set null, location b (e g , data center, carrier pop) optional endpoint b details text null, specific demarcation details for b related contract id uuid null references vendorcontracts(contract id) on delete set null, link to service contract term months integer null, install date date null, activation date date null, monthly cost numeric(19,4) null, currency code char(3) null, removed fk for flexibility notes text, use polymorphic notes table instead created at timestamptz not null default current timestamp, updated at timestamptz, unique (org id, circuit identifier, carrier id) ); comment on table telecomcircuits is 'tracks data and voice circuits (internet, wan, mpls, pri, sip trunks etc ) '; create table vpntunnels ( vpn tunnel id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, org context name varchar(100) not null, user friendly name (e g , 'office to azure', 'branch1 to hq') tunnel type varchar(20) not null check (tunnel type in ('sitetosite', 'clienttosite', 'remoteaccessuser')), status varchar(20) not null default 'active' check (status in ('planning', 'configured', 'active', 'inactive', 'down', 'error')), \ endpoint info local gateway device id uuid null references devices(device id) on delete set null, firewall/router device managing local end local gateway ip inet null, public ip of local gateway local networks cidr\[] null, local subnets accessible via tunnel remote gateway ip inet null, public ip of remote endpoint (for sitetosite) remote gateway fqdn varchar(255) null, fqdn if ip is dynamic remote gateway device id uuid null references devices(device id) on delete set null, link if remote end is also a managed device remote networks cidr\[] null, remote subnets accessible via tunnel (for sitetosite) \ client vpn specific client vpn pool cidr null, ip pool for remote access clients client auth method varchar(50) null, e g , 'certificate', 'presharedkey', 'radius', 'saml' \ tunnel configuration protocol varchar(20) default 'ipsec' check (protocol in ('ipsec', 'openvpn', 'wireguard', 'sslvpn', 'other')), ike version integer null, 1 or 2 for ipsec encryption details jsonb null, { "phase1 alg" "aes256", "phase1 hash" "sha256", "phase1 dh group" 14, "phase2 pfs group" 14, } pre shared key ref text null, vault reference for psk related circuit id uuid null references telecomcircuits(circuit id) on delete set null, underlying circuit if applicable notes text, use polymorphic notes table instead last status check timestamptz, created at timestamptz not null default current timestamp, updated at timestamptz, unique (org id, name) ); comment on table vpntunnels is 'tracks vpn tunnels (site to site, remote access) '; comment on column vpntunnels pre shared key ref is 'reference to the pre shared key stored securely (e g , in external vault or credentials table) '; \ ============================================= \ additional logging tables \ ============================================= create table storageaccesslog ( log id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, endpoint device where action occurred event timestamp timestamptz not null default current timestamp, timestamp from the endpoint when action attempted/logged user id uuid references users(user id) on delete set null, user performing the action process id integer null, optional process id performing the action process path text null, optional path of the process executable action type varchar(30) not null check (action type in ('fileread', 'filewrite', 'filedelete', 'filecopy', 'filerename', 'execute', 'foldercreate', 'folderdelete', 'folderlist', 'accessdenied')), type of operation action result varchar(10) not null check (action result in ('allowed', 'denied')), outcome based on policy storage type varchar(20) check (storage type in ('usb', 'networkshare', 'localfixed', 'localremovable', 'cddvd', 'other')), type of storage accessed device instance id varchar(255) null, optional hardware id for external/removable devices serial number varchar(255) null, optional serial number of the external device, if available drive letter or mount varchar(50) null, optional drive letter or mount point involved network share path text null, optional unc path if storage type is 'networkshare' file path text not null, the primary file/folder path involved in the action file size bytes bigint null, optional size of the file accessed/copied file hash sha256 varchar(64) null, optional sha256 hash of the file involved destination path text null, optional destination path for filecopy/filerename actions matched policy id uuid null references storagecontrolpolicies(policy id) on delete set null, the storage control policy that matched this event matched rule id uuid null references storagecontrolrules(rule id) on delete set null, the specific storage control rule that determined the action result details text null any other relevant details captured by the agent \ add indexes on timestamp, device id, user id, action result, storage type, file path (consider fts) ); create table applicationexecutionlog ( log id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, event timestamp utc timestamptz not null default current timestamp, user sid varchar(100), sid of the user attempting execution user name varchar(255), username process path text not null, full path of the executable process hash sha256 varchar(64), sha256 hash of the executable publisher info jsonb, extracted certificate/publisher info parent process path text, command line text, action taken varchar(10) not null check (action taken in ('allowed', 'denied')), result of allowlisting policy matched policy id uuid references executioncontrolpolicies(policy id) on delete set null, matched rule id uuid references executioncontrolrules(rule id) on delete set null, denial reason varchar(255) e g , 'no matching allow rule', 'explicit deny rule' \ add indexes on timestamp, device id, user name, process path, action taken ); create table elevationrequestlog ( log id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, request timestamp utc timestamptz not null default current timestamp, requesting user sid varchar(100), requesting user name varchar(255), process path text not null, application requested for elevation process hash sha256 varchar(64), command line text, requested action varchar(20) not null check (requested action in ('elevate', 'elevatewithapproval')), from matching rule approval request id uuid references approvalrequests(approval request id) on delete set null, link if approval needed justification text, user provided justification status varchar(20) not null check (status in ('pendingapproval', 'approved', 'autoapproved', 'denied', 'usercancelled', 'timeout', 'error')), status of the elevation attempt final action varchar(10) check (final action in ('allowed', 'denied')), was elevation ultimately granted? processed by user id uuid references users(user id) on delete set null, admin who approved/denied (if manual) processed timestamp timestamptz, denial reason text \ add indexes on timestamp, device id, requesting user name, process path, status ); \ ============================================= \ software asset management (sam) \ ============================================= create table softwarelicenses ( license id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, org that owns/purchased the license software product id uuid not null references softwareproducts(software product id) on delete restrict, link to the software catalog item license type varchar(30) not null check (license type in ('perpetual', 'subscription', 'cal user', 'cal device', 'oem', 'volumemak', 'volumekms', 'other')), license key text null, store securely or use vault reference if storing actual key purchased quantity integer not null check (purchased quantity >= 0), number of seats/licenses purchased license metric varchar(30) check (license metric in ('seat', 'user', 'device', 'processor', 'core', 'pvu', 'cal user', 'cal device', 'usage', 'other')), the primary metric by which this license is measured (seat, core, pvu, etc ) metric quantity numeric(19,4) null, the quantity associated with the license metric (e g , number of cores, pvu points) null if metric is seat/user/device (use purchased quantity) is upgrade boolean default false, is this an upgrade license requiring a base license? base license id uuid null references softwarelicenses(license id) on delete set null, link to required base license if is upgrade=true license pool id uuid null references softwarelicensepools(license pool id) on delete set null, link to a softwarelicensepool if this license contributes entitlements to a pool purchase date date null, expiry date date null, relevant for subscriptions purchase cost numeric(19,4) null, recurring cost numeric(19,4) null, recurring cycle varchar(20) check (recurring cycle in ('monthly', 'quarterly', 'annually')), currency code char(3) null, fk to currencycodes optional purchase order id uuid null references purchaseorders(purchase order id) on delete set null, link to po if purchased agreement id uuid null references agreements(agreement id) on delete set null, link to agreement if part of contract vendor org id uuid null references organizations(org id) on delete set null, vendor purchased from external id varchar(100), id from external asset system created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); comment on table softwarelicenses is 'tracks purchased or subscribed software license entitlements, including metric details '; comment on column softwarelicenses license key is 'stores license key if applicable (consider secure storage/vault reference) '; comment on column softwarelicenses purchased quantity is 'total number of license seats/activations purchased (primary quantity if metric is seat/user/device based) '; comment on column softwarelicenses license metric is 'the primary metric by which this license is measured (seat, core, pvu, etc ) '; comment on column softwarelicenses metric quantity is 'the quantity associated with the license metric (e g , number of cores, pvu points) relevant if metric is not seat/user/device based '; comment on column softwarelicenses license pool id is 'link to a softwarelicensepool if this license contributes entitlements to a pool '; create table softwarelicenseassignments ( assignment id bigserial primary key, license id uuid not null references softwarelicenses(license id) on delete cascade, assigned to user id uuid null references users(user id) on delete cascade, assign to user assigned to device id uuid null references devices(device id) on delete cascade, assign to device assigned date timestamptz not null default current timestamp, assignment notes text, use polymorphic notes table instead check (assigned to user id is not null or assigned to device id is not null), must assign to user or device unique (license id, assigned to user id) where assigned to user id is not null, prevent double assignment to same user unique (license id, assigned to device id) where assigned to device id is not null prevent double assignment to same device ); comment on table softwarelicenseassignments is 'links softwarelicenses to the users or devices they are assigned to '; create table softwarelicensepools ( license pool id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, org managing this pool software product id uuid not null references softwareproducts(software product id) on delete restrict, software covered by pool pool name varchar(150) not null, e g , "microsoft ees agreement pool", "adobe creative cloud pool" description text, license metric varchar(30) not null, metric for entitlements in this pool (should match related licenses) total entitlement numeric(19,4) not null default 0, calculated sum of metric quantity/purchased quantity from linked licenses \ consumed entitlement numeric(19,4) not null default 0, consider calculating this via assignments or periodic jobs notes text, use polymorphic notes table instead created at timestamptz not null default current timestamp, updated at timestamptz, unique (org id, software product id, pool name) ); comment on table softwarelicensepools is 'represents pools of license entitlements, often from volume agreements, aggregating individual licenses '; comment on column softwarelicensepools total entitlement is 'total quantity of licenses available in this pool based on linked softwarelicenses '; \ add fk constraint to softwarelicenses now that softwarelicensepools exists alter table softwarelicenses add constraint fk licenses pool foreign key (license pool id) references softwarelicensepools(license pool id) on delete set null; create table softwarelicenseevidence ( evidence id bigserial primary key, device software id bigint not null references devicesoftware(device software id) on delete cascade, the specific software installation this evidence applies to evidence type varchar(30) not null check ( evidence type in ( 'assignedentitlement', linked to a softwarelicenseassignment record 'manualkeyentry', manually entered key (retail box, documentation) 'oem coa', oem license evidenced by coa sticker 'oem bios', oem license embedded in bios/firmware 'volumemak', installation uses a specific mak key (linked via details/assignment) 'volumekms', installation activated via kms (detected key might be generic) 'unlicensed', known to be unlicensed 'licenseincludedwithhardware', e g , bundled software not tracked separately 'documentedexception', approved exception (e g , dev/test use) 'other' other documented evidence ) ), evidence details text null, store manual key (use vault ref!), coa notes, bios key presence flag, kms server info, exception reason, etc software license assignment id uuid null references softwarelicenseassignments(assignment id) on delete set null, link if evidence type is 'assignedentitlement' or relates to a specific assignment is primary evidence boolean not null default true, if multiple evidence records exist for one installation, which is the primary one used for compliance? (ensure only one true per device software id via application logic or partial index) validation status varchar(20) default 'pending' check (validation status in ('pending', 'valid', 'invalid', 'mismatch', 'requiresreview', 'notapplicable')), status of this evidence record (e g , does manual key match detected key? is assignment valid?) last validated at timestamptz, when validation status was last updated validated by user id uuid null references users(user id) on delete set null, created at timestamptz not null default current timestamp, created by user id uuid null references users(user id) on delete set null, updated at timestamptz, auto updated updated by user id uuid null references users(user id) on delete set null \ optional add a unique constraint to enforce only one primary evidence per installation \ constraint softwarelicenseevidence one primary unique (device software id, is primary evidence) where is primary evidence = true; \ note postgresql 15+ supports unique nulls not distinct which might be better if is primary evidence can be null for now, assume only one primary ); comment on table softwarelicenseevidence is 'links a specific software installation (devicesoftware) to its licensing proof or method '; comment on column softwarelicenseevidence evidence type is 'describes the nature of the licensing proof (assignment, manual key, oem, volume, exception, etc ) '; comment on column softwarelicenseevidence evidence details is 'contains the actual proof details (vaulted key reference, coa notes, exception reason, etc ) '; comment on column softwarelicenseevidence software license assignment id is 'links to the formal license assignment record if applicable '; comment on column softwarelicenseevidence is primary evidence is 'indicates if this is the primary record used for compliance determination for this installation '; comment on column softwarelicenseevidence validation status is 'indicates whether this evidence has been validated (e g , key checked, assignment verified) '; create table softwarelicenseevidenceattachments ( evidence id bigint not null references softwarelicenseevidence(evidence id) on delete cascade, link to the specific license evidence record attachment id uuid not null references attachments(attachment id) on delete cascade, link to the uploaded file/image in the attachments table description text null, optional description (e g , "photo of coa sticker", "scan of license certificate", "invoice showing purchase") attached at timestamptz not null default current timestamp, when the link was created primary key (evidence id, attachment id) a piece of evidence can have multiple attachments, and an attachment could theoretically link to multiple evidence items (though less likely) ); comment on table softwarelicenseevidenceattachments is 'links uploaded images or documents (from attachments table) as proof to softwarelicenseevidence records '; comment on column softwarelicenseevidenceattachments description is 'optional text describing the attached file in the context of the license evidence '; \ ============================================= \ saas user assignment \ ============================================= create table saasuserassignments ( saas user assignment id bigserial primary key, integration instance id uuid not null references integrationinstances(instance id) on delete cascade, the specific m365/google tenant integration user id uuid not null references users(user id) on delete cascade, the commandit user (linked to the saas user) \ optional direct links if helpful for querying \ azure ad user id uuid null references azureadusers(azure ad user id) on delete set null, \ google user id varchar(255) null, placeholder if google sync added later product id uuid not null references products(product id) on delete restrict, link to the product representing the saas sku (e g , m365 e3) assigned datetime timestamptz null, when the license was assigned in the saas platform status varchar(30) default 'active' check (status in ('active', 'suspended', 'removed', 'warning')), status from saas platform last sync time timestamptz, when this assignment info was last synced created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (integration instance id, user id, product id) user should only have one assignment of a specific sku per tenant ); comment on table saasuserassignments is 'tracks assignment of specific saas licenses/skus (represented as products) to users within integrated cloud platforms '; comment on column saasuserassignments integration instance id is 'links to the specific tenant (e g , m365 tenant integration) where the license is assigned '; comment on column saasuserassignments product id is 'links to the product table entry representing the specific saas license sku (e g , microsoft 365 e3, google workspace business standard) '; \ ============================================= \ audit log \ ============================================= create table auditlog ( audit log id bigserial primary key, event timestamp timestamptz not null default current timestamp, when the event occurred user id uuid null references users(user id) on delete set null, user performing the action (null if system) impersonator user id uuid null references users(user id) on delete set null, if an admin impersonated another user org id uuid null references organizations(org id) on delete set null, organization context of the action action type varchar(100) not null, e g , 'create', 'update', 'delete', 'login', 'logout', 'enable', 'disable', 'run script', 'approve', 'reject' target entity type varchar(50) null, e g , 'ticket', 'device', 'user', 'policy' target entity id varchar(50) null, primary key of the target entity (uuid/bigint/int stored as string) target entity name varchar(255) null, optional human readable name/identifier of the target source ip address inet null, ip address where the action originated source platform varchar(50) null, platform/os action originated from (e g , 'windows', 'macos', 'ios', 'android', 'web') source language varchar(20) null, language/locale of the source (e g , 'en us', 'fr ca') user agent text null, browser/client user agent string source geo location jsonb null, geoip lookup results { "city" " ", "region" " ", "country code" " " } change details jsonb null, stores old/new values for updates, or context/parameters for other actions status varchar(20) not null default 'success' check (status in ('success', 'failure', 'attempt')), outcome of the action failure reason text null reason if status is 'failure' \ add indexes on timestamp, user id, org id, action type, target entity type, target entity id ); comment on table auditlog is 'records significant actions, changes, logins, and logouts within the system for auditing purposes '; comment on column auditlog source platform is 'operating system or platform from which the action originated '; comment on column auditlog source language is 'language/locale setting reported by the originating client '; comment on column auditlog source geo location is 'approximate geolocation derived from the source ip address (jsonb format) '; comment on column auditlog change details is 'stores old/new values for update actions, or context/parameters for other action types (jsonb format) '; \ ============================================= \ probe \ ============================================= create table networkscanscopes ( scan scope id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, client org context for the scan name varchar(255) not null, user friendly name for this scan scope (e g , "main office subnet scan", "server vlan scan") description text, assigned location id uuid null references locations(location id) on delete set null, optional link to the primary location being scanned assigned probe device id uuid not null references devices(device id) on delete restrict, which device runs this scan (must have probe component enabled) \ scan targeting included subnets cidr\[] not null, array of subnets to actively scan (e g , \['192 168 1 0/24', '10 0 0 0/16']) excluded ips inet\[] null, array of specific ips or cidr ranges to exclude within the included subnets \ scan settings scan interval seconds integer not null default 3600, how often to repeat the scan (e g , 3600 = 1 hour) scan intensity varchar(20) default 'normal' check (scan intensity in ('low', 'normal', 'high')), hint for throttling/timing perform ping sweep boolean not null default true, basic icmp discovery perform port scan boolean not null default true, scan for common/defined open ports port scan tcp ports text null, comma separated list/range of tcp ports (e g , '21 23,80,443,3389') or null for default list port scan udp ports text null, comma separated list/range of udp ports or null for default list perform os detection boolean not null default true, attempt os fingerprinting \ credential usage flags (attempt to use credentials if provided below?) attempt snmp boolean not null default true, attempt wmi boolean not null default true, attempt ssh boolean not null default true, discover vmware boolean not null default true, attempt vmware api connection using creds discover ad boolean not null default true, attempt ad ldap query using creds \ default credentials for scan scope (links to secure credential store) default snmp credential id uuid null references credentials(credential id) on delete set null, default windows credential id uuid null references credentials(credential id) on delete set null, for wmi default ssh credential id uuid null references credentials(credential id) on delete set null, vmware credential id uuid null references credentials(credential id) on delete set null, for vcenter/esxi ad credential id uuid null references credentials(credential id) on delete set null, for ldap queries \ status is active boolean not null default true, is this scan scope enabled? last scan start time timestamptz null, last scan end time timestamptz null, last scan status varchar(20) null check (last scan status in ('success', 'partialsuccess', 'failed')), created at timestamptz not null default current timestamp, updated at timestamptz, unique(org id, name), unique(assigned probe device id, name) probe can have multiple named scopes, but names unique per probe ); comment on table networkscanscopes is 'defines network discovery/scanning tasks assigned to probe devices '; comment on column networkscanscopes assigned probe device id is 'the device id of the machine running the probe component for this scan '; comment on column networkscanscopes included subnets is 'array of cidr notations defining the ip ranges to be scanned '; comment on column networkscanscopes scan intensity is 'hint for agent regarding scan speed/aggressiveness/throttling '; comment on column networkscanscopes default snmp credential id is 'link to credentials table entry holding default snmp v1/v2c community or v3 creds for this scope '; comment on column networkscanscopes default windows credential id is 'link to credentials table entry holding default windows admin creds for wmi scans within this scope '; create table deviceopenports ( device open port id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, the device where the port was found open port integer not null check (port > 0 and port <= 65535), protocol varchar(3) not null check (protocol in ('tcp', 'udp')), service name varchar(100) null, service commonly associated (e g , 'http', 'ssh', 'rdp') based on port number or banner grab service version varchar(100) null, version information if banner grabbing was successful first seen time timestamptz not null default current timestamp, when this open port was first detected on this device last seen time timestamptz not null default current timestamp, when this open port was last confirmed present by a scan scan source probe device id uuid null references devices(device id) on delete set null, which probe detected/confirmed this is active in scan boolean not null default true, used for delta processing (detecting closed ports) unique (device id, port, protocol) \ add index on device id, last seen time ); comment on table deviceopenports is 'stores information about open tcp/udp ports discovered on devices by network probes '; comment on column deviceopenports is active in scan is 'internal flag used during delta processing to mark ports no longer detected as closed '; \ ============================================= \ service management \ ============================================= \ table to track service assignments specifically for devices create table deviceserviceassignments ( device assignment id bigserial primary key, org id uuid not null references organizations(org id) on delete cascade, client org device id uuid not null references devices(device id) on delete cascade, service category id varchar(100) not null, matches products category for the assigned product assigned product id uuid null references products(product id) on delete set null, the specific plan assigned, null if 'none' assignment method varchar(20) not null check (assignment method in ('manual', 'policy', 'default')), how was this assignment determined? policy assignment source ref text null, reference to the policy/rule causing this assignment, e g , 'tagpolicyassignment 123', 'orgdefault\ abc' override price numeric(19,4) null, specific price for this assignment, overriding calculated price override currency code varchar(3) null, currency for the override price (references currencycodes implicitly) assigned at timestamptz not null default current timestamp, assigned by user id uuid null references users(user id) on delete set null, updated at timestamptz, auto updated by trigger \ ensure only one assignment per device per category unique (device id, service category id) ); \ add indexes for performance create index idx deviceserviceassignments device on deviceserviceassignments(device id); create index idx deviceserviceassignments product on deviceserviceassignments(assigned product id); create index idx deviceserviceassignments org on deviceserviceassignments(org id); create index idx deviceserviceassignments category on deviceserviceassignments(service category id); comment on table deviceserviceassignments is 'tracks the assignment of specific service products (plans) to devices for different service categories '; comment on column deviceserviceassignments service category id is 'identifier for the service category (e g , ''antivirus'', matching products category) '; comment on column deviceserviceassignments assigned product id is 'fk to the products table for the specific service plan assigned null indicates no service (''none'') is assigned for this category '; comment on column deviceserviceassignments assignment method is 'indicates if the assignment was made manually, derived from a policy, or a default '; comment on column deviceserviceassignments policy assignment source ref is 'reference identifying the specific policy or rule that dictated this assignment (if method is ''policy'') '; comment on column deviceserviceassignments override price is 'a specific price manually set for this assignment, bypassing standard pricing rules '; \ table to track service assignments specifically for users create table userserviceassignments ( user assignment id bigserial primary key, org id uuid not null references organizations(org id) on delete cascade, client org user id uuid not null references users(user id) on delete cascade, service category id varchar(100) not null, matches products category for the assigned product assigned product id uuid null references products(product id) on delete set null, the specific plan assigned, null if 'none' assignment method varchar(20) not null check (assignment method in ('manual', 'policy', 'default')), policy assignment source ref text null, override price numeric(19,4) null, override currency code varchar(3) null, assigned at timestamptz not null default current timestamp, assigned by user id uuid null references users(user id) on delete set null, updated at timestamptz, auto updated by trigger \ ensure only one assignment per user per category unique (user id, service category id) ); \ add indexes for performance create index idx userserviceassignments user on userserviceassignments(user id); create index idx userserviceassignments product on userserviceassignments(assigned product id); create index idx userserviceassignments org on userserviceassignments(org id); create index idx userserviceassignments category on userserviceassignments(service category id); comment on table userserviceassignments is 'tracks the assignment of specific service products (plans) to users for different service categories '; \ optional but recommended table to store service recommendations create table servicerecommendations ( recommendation id bigserial primary key, org id uuid not null references organizations(org id) on delete cascade, client org context scope type varchar(20) not null check (scope type in ('organization', 'location', 'devicetype', 'usergroup', 'default')), scope of recommendation scope value varchar(255) null, orgid, locationid, devicetype name, group name/id null for 'default' service category id varchar(100) not null, category being recommended for recommended product id uuid not null references products(product id) on delete restrict, the recommended plan priority integer not null default 0, for resolving conflicting recommendations (lower = higher priority) reason text null, optional explanation for the recommendation is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated \ ensure recommendation uniqueness based on scope and category unique (org id, scope type, scope value, service category id) ); \ add indexes for performance create index idx servicerecommendations scope on servicerecommendations(org id, scope type, scope value, service category id); create index idx servicerecommendations product on servicerecommendations(recommended product id); comment on table servicerecommendations is 'stores recommended service products based on different scopes (org, location, device type, etc ) '; comment on column servicerecommendations scope type is 'defines the level at which the recommendation applies (e g , for all devices of a certain type) '; comment on column servicerecommendations scope value is 'identifier matching the scope type (e g , ''laptop'', location uuid) null if scope type is ''default'' '; comment on column servicerecommendations priority is 'priority for applying recommendations if multiple rules match (lower number wins) '; \ stores blocked senders/recipients create table emailblocklist ( blocklist id bigserial primary key, org id uuid null references organizations(org id) on delete cascade, null for global platform blocks target text not null, email address (user\@example com) or domain (@example com or example com) block type varchar(20) not null check (block type in ('inboundsender', 'outboundrecipient')), scope varchar(10) not null check (scope in ('address', 'domain')), reason text null, created by user id uuid null references users(user id) on delete set null, created at timestamptz not null default current timestamp ); create index idx emailblocklist target type on emailblocklist(target, block type); create index idx emailblocklist org target type on emailblocklist(org id, target, block type); comment on table emailblocklist is 'stores email addresses and domains blocked for inbound or outbound mail processing '; \ temporary staging table for raw inbound emails create table inboundemailprocessingqueue ( queue id bigserial primary key, message id text null unique, email's message id header, unique if available and reliable received at utc timestamptz not null default current timestamp, when the email was ingested into the queue service board email address text null, the specific commandit address email was sent to target entity type varchar(50) null, type detected from to address 'serviceboard', 'alertendpoint', 'project', 'problem', 'changerequest', 'device', 'application', 'monitoreddomain', etc target entity id varchar(50) null, corresponding id (uuid/int/bigint stored as string) of the target entity headers text null, store full or key email headers as text or jsonb body html text null, html body content, if present body text text null, plain text body content, if present raw eml storage path text null, recommended path/key to the raw eml file stored temporarily in object storage (e g , s3/azure blob with short ttl) processing status varchar(30) not null default 'received' check (processing status in ( 'received', initial state upon ingestion 'processing', picked up by ai triage agent 'failed blocklist', failed pre check sender blocked 'failed spam', failed pre check classified as spam 'failed processing', ai triage failed during processing (needs review/retry) 'complete processed', successfully processed into ticket/project/etc 'complete discarded' final state for blocked/spam before deletion trigger )), status reason text null, context for the final status (e g , 'blocked domain xyz com', 'spam score 9 5', 'processed to ticket #12345', 'error cannot identify organization') last attempt at timestamptz null, timestamp of the last processing attempt (by ai) retry count integer not null default 0, number of processing attempts made linked processing log id bigint null references emailprocessinglog(log id) on delete set null, link to the final metadata log entry created after processing/discarding \ link to the permanent record created (only one should be relevant per processed email) linked ticket update id bigint null references ticketupdates(update id) on delete set null, linked project update id bigint null references projectupdates(project update id) on delete set null, linked problem update id bigint null references problemupdates(problem update id) on delete set null, linked change update id bigint null references changerequestupdates(change update id) on delete set null ); \ indexes for efficient querying by status and potentially for duplicate message id checks create index idx inbound email queue status on inboundemailprocessingqueue(processing status, received at utc); create index idx inbound email queue message id on inboundemailprocessingqueue(message id) where message id is not null; create index idx inbound email queue target on inboundemailprocessingqueue(target entity type, target entity id); comment on table inboundemailprocessingqueue is 'temporary staging table for raw inbound emails awaiting ai triage processing records and associated raw content (e g , in object storage) are deleted after successful processing or discarding based on status '; comment on column inboundemailprocessingqueue message id is 'email message id header, used for potential deduplication if available and reliable '; comment on column inboundemailprocessingqueue target entity type is 'type of commandit entity the inbound email address maps to (serviceboard, project, device, monitoreddomain etc ) determined on receipt '; comment on column inboundemailprocessingqueue target entity id is 'the primary key (uuid, bigint etc , stored as string) of the specific entity the inbound email address maps to '; comment on column inboundemailprocessingqueue raw eml storage path is 'recommended path/key to raw eml file stored temporarily in object storage (e g , s3/azure blob with short ttl) storing large raw content directly in db is less ideal '; comment on column inboundemailprocessingqueue processing status is 'lifecycle status of the email within the temporary queue '; comment on column inboundemailprocessingqueue status reason is 'provides context for the final status before deletion or for identifying processing errors '; comment on column inboundemailprocessingqueue linked processing log id is 'fk to the permanent emailprocessinglog entry created when this queue item reached a final state '; comment on column inboundemailprocessingqueue linked ticket update id is 'fk to the ticketupdates record where the processed email content was logged (if applicable) '; comment on column inboundemailprocessingqueue linked project update id is 'fk to the projectupdates record where the processed email content was logged (if applicable) '; comment on column inboundemailprocessingqueue linked problem update id is 'fk to the problemupdates record where the processed email content was logged (if applicable) '; comment on column inboundemailprocessingqueue linked change update id is 'fk to the changerequestupdates record where the processed email content was logged (if applicable) '; \ short term log of email processing outcomes create table emailprocessinglog ( log id bigserial primary key, log timestamp utc timestamptz not null default current timestamp, direction varchar(10) not null check (direction in ('inbound', 'outbound')), message id text null, email's message id header from address text not null, to addresses text\[] null, cc addresses text\[] null, subject text null, status varchar(30) not null check (status in ( 'processed newticket', 'processed updatedticket', 'processed projectupdate', 'blocked sender', 'blocked recipient', 'discarded spam', 'discarded rule', 'sent notification', 'failed processing', 'failed sending' )), added discarded rule, processed projectupdate related ticket id bigint null references tickets(ticket id) on delete set null, related project id uuid null references projects(project id) on delete set null, added project link related service board id uuid null references serviceboards(board id) on delete set null, related user id uuid null references users(user id) on delete set null, related contact id uuid null references contacts(contact id) on delete set null, error message text null, processing agent varchar(50) not null e g , 'ai email triage', 'commandit notification service' ); create index idx emailproc log timestamp on emailprocessinglog(log timestamp utc); essential for 7 day cleanup create index idx emailproc log status on emailprocessinglog(status); create index idx emailproc log ticket on emailprocessinglog(related ticket id); create index idx emailproc log project on emailprocessinglog(related project id); create index idx emailproc log message id on emailprocessinglog(message id); comment on table emailprocessinglog is 'short term (7 day rolling) log of inbound/outbound email processing outcomes '; create table alertingestionendpoints ( endpoint id uuid primary key default gen random uuid(), org id uuid null references organizations(org id) on delete cascade, specific org scope location id uuid null references locations(location id) on delete cascade, specific location scope email address varchar(255) not null unique, the {guid}@commandit net address description text null, purpose of this endpoint target board id uuid null references serviceboards(board id) on delete set null, optional default board if rule doesn't specify processing policy id uuid null references alertprocessingpolicies(policy id) on delete set null, default policy to apply is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, \ ensure scope is defined correctly (org or location, but not both, allows neither for global?) constraint chk alert endpoint scope check ( (org id is not null and location id is null) or (location id is not null and org id is not null) or (org id is null and location id is null) ) refined allow global (no scope), org only, or location (implies org) ); comment on table alertingestionendpoints is 'stores dedicated email addresses for receiving alerts, scoped globally, to orgs, or locations '; create table alertprocessingrules ( rule id uuid primary key default gen random uuid(), owner org id uuid null references organizations(org id) on delete cascade, msp or platform (null) defining the rule base name varchar(255) not null, description text, condition logic jsonb not null, structured conditions for matching alerts/emails alert signature definition jsonb null, defines how to build the unique signature for state tracking clear condition logic jsonb null, defines conditions for a clearing alert/email is system defined boolean generated always as (owner org id is null) stored, created at timestamptz not null default current timestamp, updated at timestamptz, unique nulls not distinct (owner org id, name) ); comment on table alertprocessingrules is 'defines conditions for matching incoming alerts/emails and how to uniquely identify alert conditions '; comment on column alertprocessingrules condition logic is 'jsonb defining conditions to match an alert structure { "match operator" "and|or", "criteria" \[ {"field" " ", "operator" " ", "value" " "} ] } '; comment on column alertprocessingrules alert signature definition is 'jsonb defining how to construct a unique alert signature, e g , \["field\ device id", "field\ check name"] or {"regex capture" "subject alert for ( ?) on ( ?)"} '; comment on column alertprocessingrules clear condition logic is 'optional jsonb (same structure as condition logic) defining conditions that signify this alert has cleared '; \ groups rules into policies, defines action & inheritance create table alertprocessingpolicies ( policy id uuid primary key default gen random uuid(), owner org id uuid null references organizations(org id) on delete cascade, msp or platform (null) defining policy name varchar(255) not null, description text, \ scope where this definition applies (overrides based on assignment) scope type varchar(20) not null check (scope type in ('global', 'organization', 'location')), scope id uuid null, link to orgid/locationid if scoped definition parent policy id uuid null references alertprocessingpolicies(policy id) on delete set null, for inheritance chain is active boolean not null default true, created at timestamptz not null default current timestamp, updated at timestamptz, unique nulls not distinct (owner org id, name, scope type, scope id) ); comment on table alertprocessingpolicies is 'groups alertprocessingrules and defines action, scope, and inheritance for email/alert triage '; \ add fk constraint from alertingestionendpoints to alertprocessingpolicies alter table alertingestionendpoints add constraint fk alertendpoints policy foreign key (processing policy id) references alertprocessingpolicies(policy id) on delete set null; \ add direct links on org/location for policy assignment (can also use tags) alter table organizations add column alert processing policy id uuid null references alertprocessingpolicies(policy id) on delete set null; alter table locations add column alert processing policy id uuid null references alertprocessingpolicies(policy id) on delete set null; create table alertprocessingpolicyrules ( policy rule id bigserial primary key, policy id uuid not null references alertprocessingpolicies(policy id) on delete cascade, rule id uuid not null references alertprocessingrules(rule id) on delete cascade, priority integer not null default 0, order of rule evaluation (lower runs first) action varchar(30) not null check (action in ( 'createticket', 'ignore', 'routetoboard', 'updateexistingci', 'runscript', 'sendnotification', 'callwebhook' )), action if rule condition matches action parameters jsonb null, parameters for the action, including thresholds is active boolean not null default true, enable/disable this specific rule within the policy unique(policy id, rule id), unique(policy id, priority) ); comment on table alertprocessingpolicyrules is 'links rules to policies, defining the action, parameters, thresholds, and priority for matching rules '; comment on column alertprocessingpolicyrules action is 'the action to take if the associated rule conditions are met and thresholds passed '; comment on column alertprocessingpolicyrules action parameters is $$jsonb storing parameters for the specified action and optional thresholds common thresholds (optional) "trigger after occurrences" (integer, default 1) trigger action after x occurrences "trigger occurrence window seconds" (integer, default 0) time window for occurrence count "trigger after duration seconds" (integer, default 0) trigger if alert active for z seconds action 'createticket' "ticket template id" (string uuid, required) "priority id" (integer, optional), "status id" (string uuid, optional), "assigned user id" (string uuid, optional), "target board id" (string uuid, optional) action 'routetoboard' "target board id" (string uuid, required) action 'updateexistingci' "ci identifier field" (string, required), "ci identifier regex" (string, optional), "ci type" (string, required), "max ticket age days" (integer, optional, default 30), "update status to" (string uuid, optional), "add internal note" (boolean, optional, default true) action 'runscript' "script id" (string uuid, required) "script parameters" (jsonb, optional) "target device context" (string, optional, default 'alertdevice') 'alertdevice', 'specificdevice', 'probedevice' "specific device id" (string uuid, optional) required if target is 'specificdevice' action 'sendnotification' "notification profile id" (string uuid, required) "notification template id override" (string uuid, optional) action 'callwebhook' "webhook url" (string, required) "webhook method" (string, optional, default 'post') "webhook payload template" (jsonb, optional) "webhook headers" (jsonb, optional) $$; create table ciemailmappings ( email address varchar(255) primary key, the unique {guid}@commandit net address ci type varchar(50) not null, type of the linked ci (e g , 'device', 'monitoreddomain', 'application', 'telecomcircuit') ci id varchar(50) not null, the primary key of the ci record (uuid/bigint stored as varchar) org id uuid not null references organizations(org id) on delete cascade, org context for the ci generated at timestamptz not null default current timestamp, generated by user id uuid null references users(user id) on delete set null \ optional add is active flag if needed ); \ index for quick lookup based on ci create index idx ciemailmappings ci on ciemailmappings(ci type, ci id); \ index for quick lookup based on org create index idx ciemailmappings org on ciemailmappings(org id); comment on table ciemailmappings is 'maps dedicated inbound email addresses to specific configuration items (devices, domains, apps, etc ) '; comment on column ciemailmappings email address is 'the unique {guid}@commandit net address generated for the ci '; comment on column ciemailmappings ci type is 'indicates the type of commandit entity this email maps to (matches table names or defined types) '; comment on column ciemailmappings ci id is 'the primary key (uuid, bigint etc , stored as string) of the specific ci record '; create table filesystempermissions ( fs permission id bigserial primary key, target entity type varchar(50) not null check (target entity type in ('devicelogicaldisk', 'networkshare')), e g , volume c on device x, or share y target entity id varchar(50) not null, uuid of devicelogicaldisk or networkshare path text not null, specific file or folder path within the target entity account sid varchar(100) null, security identifier (sid) of the principal (user/group) account name varchar(255) not null, name of the principal access type varchar(10) not null check (access type in ('allow', 'deny')), permissions text\[] not null, array of granular permissions (e g , 'readdata', 'writedata', 'appenddata', 'readextendedattributes', 'writeextendedattributes', 'executefile', 'deletesubdirectoriesandfiles', 'readattributes', 'writeattributes', 'delete', 'readpermissions', 'changepermissions', 'takeownership', 'synchronize', 'fullcontrol') inheritance flags text\[] null, e g , \['objectinherit', 'containerinherit', 'nopropagateinherit', 'inheritonly'] is inherited boolean not null default false, last seen time timestamptz not null default current timestamp, scan source probe device id uuid null references devices(device id) on delete set null, unique (target entity type, target entity id, path, account sid, access type) approximate uniqueness ); comment on table filesystempermissions is 'stores detailed file system access control list (acl) permissions for files/folders on devices or shares '; comment on column filesystempermissions target entity type is 'type of entity the path belongs to (e g , a specific logical disk on a device, or a network share) '; comment on column filesystempermissions target entity id is 'identifier (uuid/pk) of the logical disk or network share '; comment on column filesystempermissions path is 'the specific file or folder path relative to the target entity '; comment on column filesystempermissions account sid is 'sid of the user or group the permission applies to '; comment on column filesystempermissions permissions is 'array of granular file system permissions granted or denied '; comment on column filesystempermissions inheritance flags is 'flags indicating how this permission is inherited by child objects/containers '; comment on column filesystempermissions is inherited is 'indicates if this specific ace was inherited from a parent object '; create table devicepatchstatus ( device patch status id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, patch definition id uuid not null references patchdefinitions(patch definition id) on delete cascade, status varchar(30) not null check (status in ('needed', 'installed', 'approvedforinstall', 'pendingreboot', 'failed', 'superseded', 'notapplicable', 'unknown')), installed on timestamptz null, when the patch was successfully installed last detected needed on timestamptz null, when the agent last saw this patch as needed last attempted install on timestamptz null, timestamp of last install attempt (success or fail) last scan time timestamptz not null default current timestamp, when this status was last determined by a scan source scan id varchar(100) null, identifier from the specific scan that reported this status notes text null, unique (device id, patch definition id) ); comment on table devicepatchstatus is 'tracks the installation status of specific patches on individual devices '; comment on column devicepatchstatus status is 'current status of the patch on the device (needed, installed, failed, etc ) '; create table devicescheduledtasks ( device task id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, task name text not null, full path/name of the task status varchar(30) check (status in ('ready', 'running', 'disabled', 'queued', 'unknown')), is enabled boolean, last run time timestamptz null, last run result integer null, exit code of the last run next run time timestamptz null, actions jsonb null, details of actions (e g , executable path, arguments) triggers jsonb null, details of triggers (e g , time, logon, event) run as user varchar(255) null, last seen time timestamptz not null default current timestamp, unique (device id, task name) ); comment on table devicescheduledtasks is 'stores information about scheduled tasks discovered on devices '; comment on column devicescheduledtasks task name is 'the full name or path identifying the scheduled task '; comment on column devicescheduledtasks last run result is 'the exit code returned by the last execution of the task '; create table dnsrecords ( dns record id bigserial primary key, source domain id uuid null references addomains(ad domain id) on delete set null, link if discovered via ad dns source monitor id uuid null references monitoreddomains(domain id) on delete set null, link if discovered via external monitor zone name varchar(255) not null, the dns zone the record belongs to (e g , 'ahsn local', 'example com') record name varchar(255) not null, the record name (e g , '@', 'www', 'mail') record type varchar(10) not null check (record type in ('a', 'aaaa', 'cname', 'mx', 'txt', 'srv', 'ns', 'ptr', 'soa', 'caa', 'other')), record value text not null, the value/target of the record ttl integer null, time to live in seconds mx preference integer null, mx record preference (only applicable if record type='mx') srv priority integer null, srv record priority srv weight integer null, srv record weight srv port integer null, srv record port srv target varchar(255) null, srv record target hostname last seen time timestamptz not null default current timestamp, source description varchar(100), how was this record found ('ad dns', 'external query', 'manual') unique (zone name, record name, record type, record value, mx preference, srv priority, srv weight, srv port) attempt at uniqueness ); comment on table dnsrecords is 'stores details about dns records discovered within specific zones, either internal (ad) or external '; create index idx dnsrecords zone name on dnsrecords(zone name); create index idx dnsrecords record name on dnsrecords(record name); create index idx dnsrecords record type on dnsrecords(record type); create table internetspeedtestresults ( speed test result id bigserial primary key, source device id uuid null references devices(device id) on delete set null, device that ran the test (e g , probe) source location id uuid null references locations(location id) on delete set null, location context if not device specific org id uuid not null references organizations(org id) on delete cascade, org context test timestamp utc timestamptz not null default current timestamp, download mbps numeric(10, 2) null, upload mbps numeric(10, 2) null, latency ms integer null, jitter ms integer null, packet loss percent numeric(5, 2) null, test server details jsonb null, { "name" " ", "location" " ", "ip" " ", "provider" " " } test provider varchar(100) null e g , 'speedtest net', 'fast com', 'internaltool' ); comment on table internetspeedtestresults is 'stores results from internet speed tests '; create table webcontentfilteringresults ( web filter result id bigserial primary key, source device id uuid null references devices(device id) on delete set null, device where browse occurred source user id uuid null references users(user id) on delete set null, user browse org id uuid not null references organizations(org id) on delete cascade, org context integration instance id uuid null references integrationinstances(instance id) on delete set null, link to content filter integration event timestamp utc timestamptz not null, requested url text not null, resolved ip inet null, action taken varchar(20) not null check (action taken in ('allowed', 'blocked', 'warned', 'overridden')), reason varchar(255) null, reason for block/warn (e g , 'policyviolation', 'threatdetected') category name varchar(100) null, category identified by the filter (e g , 'socialmedia', 'malware', 'phishing') threat name varchar(100) null, specific threat if identified source ip address inet null, ip of the browse device at the time created at timestamptz not null default current timestamp ); comment on table webcontentfilteringresults is 'stores logs from web content filtering systems '; create table localsecuritypolicysettings ( local policy setting id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, policy category varchar(100) not null, e g , 'passwordpolicy', 'accountlockoutpolicy', 'auditpolicy', 'userrightsassignment', 'securityoptions' policy name varchar(255) not null, specific policy name (e g , 'minimumpasswordlength', 'auditaccountlogonevents', 'accessthiscomputerfromnetwork') setting value text null, the actual setting value detected on the machine setting value type varchar(20) default 'string', helps interpret setting value (e g , 'string', 'integer', 'boolean', 'stringarray') source varchar(100) null, how setting was derived ('localpolicy', 'effectivegpo', 'registry') source gpo name varchar(255) null, name of gpo if source is 'effectivegpo' last assessment time timestamptz not null default current timestamp, unique (device id, policy category, policy name) ); comment on table localsecuritypolicysettings is 'stores detailed local security policy settings detected on individual devices '; comment on column localsecuritypolicysettings policy category is 'high level category of the security policy (password, audit, etc ) '; comment on column localsecuritypolicysettings policy name is 'the specific name of the policy setting '; comment on column localsecuritypolicysettings setting value is 'the value of the policy setting as detected on the device '; create table devicecomplianceresults ( compliance result id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, compliance rule id uuid not null references compliancerules(rule id) on delete cascade, check timestamp utc timestamptz not null default current timestamp, result varchar(20) not null check (result in ('compliant', 'noncompliant', 'error', 'notapplicable')), result details text null, specific value found or error message remediation status varchar(20) null check (remediation status in ('pending', 'running', 'success', 'failed', 'skipped', 'notattempted')), status of automated remediation attempt if triggered remediation attempted at timestamptz null, timestamp when remediation was last attempted remediation log text null, optional log output captured from the remediation script/command remediation command id bigint null references agentcommandqueue(command queue id) on delete set null, link to the specific command queue entry that performed the remediation related alert id bigint null references alerts(alert id) on delete set null, related ticket id bigint null references tickets(ticket id) on delete set null, created at timestamptz not null default current timestamp, when the result record was first created updated at timestamptz auto updated when status/remediation fields change ); \ comments comment on table devicecomplianceresults is 'stores the results of compliance rule checks against specific devices, including status of any automated remediation attempts '; comment on column devicecomplianceresults result is 'the outcome of the compliance check (compliant, noncompliant, error, notapplicable) '; comment on column devicecomplianceresults result details is 'specific value found during the check or error message if the check failed '; comment on column devicecomplianceresults remediation status is 'tracks the status of automated remediation efforts linked to this compliance finding '; comment on column devicecomplianceresults remediation attempted at is 'timestamp of the last automated remediation attempt for this finding '; comment on column devicecomplianceresults remediation log is 'optional log output captured from the executed remediation script or command '; comment on column devicecomplianceresults remediation command id is 'links to the specific command executed via agentcommandqueue for remediation '; \ indexes create index idx devicecomplianceresults device rule on devicecomplianceresults(device id, compliance rule id); create index idx devicecomplianceresults result time on devicecomplianceresults(result, check timestamp utc); create index idx devicecomplianceresults remediation status on devicecomplianceresults(remediation status); create table deviceperformancesnapshots ( snapshot id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, timestamp utc timestamptz not null, cpu usage percent numeric(5, 2) null, ram usage percent numeric(5, 2) null, ram used mb integer null, \ add other key metrics as needed (disk io, network throughput per nic, etc ) constraint device perf snap device time unique unique (device id, timestamp utc) ); comment on table deviceperformancesnapshots is 'stores periodic snapshots of key device performance metrics note can grow very large; consider time series db for high frequency '; create table networkportmetrics ( port metric id bigserial primary key, port id uuid not null references networkdeviceports(port id) on delete cascade, timestamp utc timestamptz not null, bytes in bigint null, bytes out bigint null, packets in bigint null, packets out bigint null, errors in bigint null, errors out bigint null, discards in bigint null, discards out bigint null, \ add other relevant metrics (queue length, broadcast packets, etc ) constraint net port metric port time unique unique (port id, timestamp utc) ); comment on table networkportmetrics is 'stores historical network interface metrics for ports on network devices note can grow very large; consider time series db for high frequency '; create table cistatushistory ( status history id bigserial primary key, ci type varchar(50) not null, e g , 'device', 'sqlserverinstance', 'monitoredwebsite', 'vpntunnel' ci id varchar(50) not null, uuid or other pk stored as string timestamp utc timestamptz not null default current timestamp, old status varchar(50) null, new status varchar(50) not null, change reason text null, e g , 'monitoring check', 'user action', 'system event' changed by ref varchar(100) null e g , 'user\ uuid', 'rule\ uuid', 'agent' ); comment on table cistatushistory is 'generic table to track status changes for various configuration items over time '; create index idx cistatushistory ci on cistatushistory(ci type, ci id, timestamp utc); create table firewallpolicies ( firewall policy id uuid primary key default gen random uuid(), managing device id uuid not null references devices(device id) on delete cascade, the firewall device this policy resides on policy name varchar(255) not null, description text, is active boolean default true, last sync time timestamptz, unique (managing device id, policy name) ); comment on table firewallpolicies is 'represents named firewall policies or rule sets on a firewall device '; create table firewallrules ( firewall rule id bigserial primary key, firewall policy id uuid null references firewallpolicies(firewall policy id) on delete cascade, optional link to a policy group managing device id uuid not null references devices(device id) on delete cascade, firewall device rule name varchar(255) null, name/identifier of the rule sequence order integer not null default 0, source zone varchar(100), source addresses text\[], ips, cidrs, address objects/groups destination zone varchar(100), destination addresses text\[], ips, cidrs, address objects/groups services text\[], protocol/port, service objects/groups (e g , 'tcp/443', 'http', 'dns') action varchar(10) not null check (action in ('allow', 'deny', 'reject')), is enabled boolean default true, logging enabled boolean default false, description text, last sync time timestamptz, unique (managing device id, rule name, sequence order) approximate uniqueness ); comment on table firewallrules is 'stores individual firewall rules discovered or configured on firewall devices '; create table endpointprotectionstatus ( ep status id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, product name varchar(255) not null, e g , 'microsoft defender', 'sentinelone agent' product version varchar(50) null, engine version varchar(50) null, definition version varchar(100) null, last definition update timestamptz null, realtime protection status varchar(30) null check (realtime protection status in ('enabled', 'disabled', 'snoozed', 'expired', 'error', 'notreporting', 'unknown')), adjusted statuses firewall status varchar(30) null check (firewall status in ('enabled', 'disabled', 'error', 'notreporting', 'unknown', 'notapplicable')), adjusted statuses last full scan time timestamptz null, last quick scan time timestamptz null, detected threats count integer default 0, based on recent threat events, maybe reset periodically configuration details jsonb null, added field for detailed defender config (populated by agent) last seen time timestamptz not null default current timestamp, unique (device id, product name) ); comment on table endpointprotectionstatus is 'stores status information about endpoint security products (av/epp) detected via os mechanisms or specific integrations '; comment on column endpointprotectionstatus realtime protection status is 'operational status of real time protection component (derived from wmi productstate or vendor tools) '; comment on column endpointprotectionstatus configuration details is 'jsonb field storing detailed configuration settings (currently populated primarily for microsoft defender via get mppreference) '; create index idx endpointprotectionstatus device id on endpointprotectionstatus(device id); create table fileintegritybaselines ( fim baseline id uuid primary key default gen random uuid(), policy id uuid not null, link to a fim policy or compliance policy/rule target entity type varchar(50) not null, e g , 'device', 'tag' target entity id varchar(50) not null, file or directory path text not null, expected hash sha256 varchar(64) null, for file baselining monitor attributes boolean default true, monitor changes to attributes (permissions, owner, timestamps) monitor content boolean default true, monitor changes to content (hash) is active boolean default true, created at timestamptz not null default current timestamp ); comment on table fileintegritybaselines is 'defines baseline paths and expected states for file integrity monitoring '; create table fileintegrityalerts ( fim alert id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, fim baseline id uuid null references fileintegritybaselines(fim baseline id) on delete set null, link to the baseline violated alert timestamp utc timestamptz not null default current timestamp, file path text not null, change type varchar(30) not null check (change type in ('contentmodified', 'attributesmodified', 'filecreated', 'filedeleted', 'filerenamed')), old value text null, e g , old hash, old attributes new value text null, e g , new hash, new attributes process path text null, process that made the change (if available) user name varchar(255) null, user context of the change (if available) status varchar(20) default 'new' check (status in ('new', 'acknowledged', 'investigating', 'resolved', 'falsepositive')), related ticket id bigint null references tickets(ticket id) on delete set null ); comment on table fileintegrityalerts is 'logs detected violations against file integrity monitoring baselines '; create table applicationdependency ( dependency id bigserial primary key, parent app id uuid not null references applications(application id) on delete cascade, the application that has the dependency child ci type varchar(50) not null, type of ci the parent depends on (e g , 'application', 'sqlserverinstance', 'device', 'networkshare') child ci id varchar(50) not null, id of the dependent ci dependency type varchar(50) default 'runson', e g , 'runson', 'usesdatabase', 'consumesapi', 'requiresservice' criticality varchar(20) null check (criticality in ('veryhigh', 'high', 'medium', 'low')), how critical is this dependency for the parent app? notes text, discovered at timestamptz default current timestamp, discovered by varchar(50), 'manual', 'scan', 'import' unique (parent app id, child ci type, child ci id, dependency type) ); comment on table applicationdependency is 'maps dependencies between logical applications and other configuration items '; create table racks ( rack id uuid primary key default gen random uuid(), location id uuid not null references locations(location id) on delete cascade, where the rack physically is name varchar(100) not null, user defined name (e g , 'rack a 01', 'server rack 3') u height integer not null check (u height > 0), total usable u height manufacturer id uuid null references manufacturers(manufacturer id) on delete set null, model varchar(100) null, serial number varchar(100) null, asset tag varchar(100) null, notes text, created at timestamptz not null default current timestamp, updated at timestamptz, unique(location id, name) ); comment on table racks is 'represents physical equipment racks within locations '; create table electricalcircuits ( circuit id uuid primary key default gen random uuid(), location id uuid not null references locations(location id) on delete cascade, location of the panel panel name varchar(100) not null, identifier for the electrical panel circuit number panel varchar(50) not null, breaker number/identifier within the panel breaker rating amps integer not null, voltage integer not null, e g , 120, 208, 240 phase varchar(10) check (phase in ('single', 'three')), description text null, e g , 'ups input a feed', 'rack 3 bottom pdu' is active boolean default true, created at timestamptz not null default current timestamp, updated at timestamptz, unique (location id, panel name, circuit number panel) ); comment on table electricalcircuits is 'represents individual electrical circuits originating from panels '; create table poweroutlets ( outlet id uuid primary key default gen random uuid(), location id uuid not null references locations(location id) on delete cascade, location of the outlet/pdu/ups outlet type varchar(10) not null check (outlet type in ('wall', 'pdu', 'ups')), type of outlet source device id uuid null references devices(device id) on delete set null, link to the pdu or ups device this outlet is on null if wall circuit id uuid null references electricalcircuits(circuit id) on delete set null, circuit feeding this outlet (directly for wall, or feeding the pdu/ups) outlet label varchar(100) not null, user defined or pdu/ups label (e g , 'wall 101 a', 'pdu a\ c5', 'ups b\ out3') plug type receptacle varchar(50) not null, type of receptacle (e g , 'nema 5 15r', 'nema l6 30r', 'iec c13') notes text, created at timestamptz not null default current timestamp, updated at timestamptz, unique (location id, outlet label), label should be unique within a location unique (source device id, outlet label) where source device id is not null label should be unique on a specific pdu/ups ); comment on table poweroutlets is 'represents physical power outlets (wall, pdu, ups), linking them to locations, circuits, and source devices (pdu/ups) '; comment on column poweroutlets outlet type is 'specifies if this is a wall outlet, or an outlet on a pdu or ups '; comment on column poweroutlets source device id is 'if outlet type is pdu or ups, this links to the specific device record for that pdu/ups '; comment on column poweroutlets circuit id is 'the building electrical circuit ultimately feeding this outlet or the pdu/ups it resides on '; comment on column poweroutlets outlet label is 'the physical or logical label identifying this specific outlet '; comment on column poweroutlets plug type receptacle is 'the type of receptacle/socket (e g , nema 5 15r) '; create index idx poweroutlets location on poweroutlets(location id); create index idx poweroutlets circuit on poweroutlets(circuit id); create index idx poweroutlets source device on poweroutlets(source device id); create table poweroutletattachments ( outlet id uuid not null references poweroutlets(outlet id) on delete cascade, attachment id uuid not null references attachments(attachment id) on delete cascade, description text null, optional describe the picture (e g , 'close up of nema l6 30r receptacle', 'photo of label') is primary image boolean not null default false, optional flag one image as the primary one for this outlet attached at timestamptz not null default current timestamp, primary key (outlet id, attachment id) ); comment on table poweroutletattachments is 'links attachments (like pictures) stored in the attachments table to specific poweroutlets records '; create table devicepowersupplies ( power supply id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, psu label varchar(50) not null, user friendly label within the device (e g , 'psu 1', 'power supply a') input label varchar(50) null, label for the specific input port on this psu (e g , 'input 1', 'ac input') manufacturer id uuid null references manufacturers(manufacturer id) on delete set null, model varchar(100) null, serial number varchar(100) null, part number varchar(100) null, wattage rating integer null, rated wattage capacity input type varchar(10) null check (input type in ('ac', 'dc')), type of power input input voltage range varchar(50) null, e g , '100 240v' input plug type varchar(50) null, type of plug on the psu's cord (e g , 'iec c14', 'nema 5 15p') status varchar(30) null check (status in ('ok', 'failed', 'warning', 'notpresent', 'unknown')), operational status last status check timestamptz null, notes text, created at timestamptz not null default current timestamp, updated at timestamptz, unique (device id, psu label, input label) unique input per psu per device ); comment on table devicepowersupplies is 'tracks individual power supply units (psus) within devices and their inputs '; comment on column devicepowersupplies psu label is 'identifier for the psu within the device (e g , psu 1, psu 2) '; comment on column devicepowersupplies input label is 'identifier for the specific power input port on this psu, if it has multiple (e g , input 1, input a) '; comment on column devicepowersupplies status is 'operational status reported by the system (via snmp, agent, etc ) '; create table powerconnections ( power connection id bigserial primary key, power source outlet id uuid not null references poweroutlets(outlet id) on delete cascade, the outlet providing power powered device psu id bigint not null references devicepowersupplies(power supply id) on delete cascade, the specific psu input receiving power connection timestamp timestamptz not null default current timestamp, when this connection was documented/made notes text, unique (power source outlet id), an outlet should only feed one input directly unique (powered device psu id) a psu input should only receive power from one outlet directly ); comment on table powerconnections is 'maps power connections from a source outlet (wall, pdu, ups) to a specific device power supply input '; comment on column powerconnections power source outlet id is 'fk to the poweroutlets record providing the power '; comment on column powerconnections powered device psu id is 'fk to the specific devicepowersupplies record (representing a psu input port) receiving the power '; \ needed for data breach liability summary report create table piifindings ( pii finding id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, device where pii was found org id uuid not null references organizations(org id) on delete cascade, org context location path text not null, full path to the file or data store containing pii pii type detected varchar(100) not null, type of pii found (e g , 'creditcardnumber', 'ssn', 'sin', 'driverslicense bc', 'passportnumber') match count integer default 1, how many instances found in this location match context text null, optional a snippet showing the context of the match (use carefully due to sensitivity) detection source varchar(100) null, tool or method used for detection (e g , 'agentscan dlppattern', 'manualreview') detection timestamp timestamptz not null default current timestamp, last seen timestamp timestamptz not null default current timestamp, status varchar(30) default 'new' check (status in ('new', 'reviewed', 'remediated', 'falsepositive', 'riskaccepted')), status updated by uuid null references users(user id) on delete set null, status updated at timestamptz null, related ticket id bigint null references tickets(ticket id) on delete set null, notes text null ); comment on table piifindings is 'stores findings related to the discovery of personally identifiable information (pii) on managed assets '; comment on column piifindings location path is 'the full path (e g , file path, database table/column) where the pii was detected '; comment on column piifindings pii type detected is 'the specific type or category of pii that was found '; comment on column piifindings match context is 'an optional, redacted snippet showing the context of the detected pii (use with extreme caution due to data sensitivity) '; create index idx piifindings device on piifindings(device id); create index idx piifindings org type on piifindings(org id, pii type detected); create index idx piifindings status on piifindings(status); create table securitythreatevents ( threat event id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, org id uuid not null references organizations(org id) on delete cascade, denormalized from device detection timestamp utc timestamptz not null, threat name varchar(255) not null, threat type varchar(50) null, e g , 'malware', 'pup', 'ransomware', 'exploit' file path text null, path of the affected file, if applicable file hash sha256 varchar(64) null, process path text null, path of the responsible process, if applicable source ip inet null, source ip if network based threat destination ip inet null, destination ip if network based threat action taken varchar(50) null, e g , 'cleaned', 'quarantined', 'deleted', 'blocked', 'detectedonly' status varchar(20) default 'new' check (status in ('new', 'investigating', 'remediated', 'closed', 'falsepositive')), detection source varchar(100) null, e g , 'sophos endpoint', 'windows defender', 'edr agent x' created at timestamptz not null default current timestamp ); comment on table securitythreatevents is 'logs specific threat detection events from endpoint security products (av/edr) '; create index idx secthreatevents time on securitythreatevents(detection timestamp utc); create index idx secthreatevents device on securitythreatevents(device id); create index idx secthreatevents org time on securitythreatevents(org id, detection timestamp utc); \ revised to consolidate external service and power/infrastructure incidents create table externalserviceincidents ( ext incident id bigserial primary key, org id uuid not null references organizations(org id) on delete cascade, org reporting/affected by the incident incident type varchar(30) not null check (incident type in ('externalservice', 'poweroutage', 'internalinfrastructure', 'other')), type of incident location id uuid null references locations(location id) on delete set null, location affected (primarily for poweroutage/internalinfrastructure) service name varchar(255) not null, name of the affected service (e g , 'm365', 'salesforce') or description for infrastructure issues (e g , 'utility power outage main office', 'building hvac failure') vendor org id uuid null references organizations(org id) on delete set null, link to vendor org (for externalservice) or utility (for poweroutage) if tracked status varchar(30) not null check (status in ('investigating', 'identified', 'monitoring', 'resolved', 'falsealarm', 'declared')), added 'declared' for potential use reason text null, specific reason, especially for power/infrastructure (e g , 'utility failure', 'ups battery exhausted', 'cooling unit failure') start time utc timestamptz not null, end time utc timestamptz null, duration seconds integer null, can be calculated if end time utc is set impact description text, resolution summary text null, reported by user id uuid null references users(user id) on delete set null, related ticket id bigint null references tickets(ticket id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz ); comment on table externalserviceincidents is 'tracks significant incidents/outages related to key third party services or internal infrastructure like power failures '; comment on column externalserviceincidents incident type is 'categorizes the type of incident being logged '; comment on column externalserviceincidents location id is 'the specific location affected, primarily used for poweroutage or internalinfrastructure types '; comment on column externalserviceincidents service name is 'name of the affected external service, or a descriptive name for infrastructure issues (e g , "power outage site x") '; comment on column externalserviceincidents vendor org id is 'link to the organization record for the external service provider or utility company, if applicable '; comment on column externalserviceincidents reason is 'specific cause or reason, particularly relevant for non service related incidents '; \ add relevant indexes create index idx extsvcinc org type time on externalserviceincidents(org id, incident type, start time utc); create index idx extsvcinc location on externalserviceincidents(location id) where location id is not null; create index idx extsvcinc status on externalserviceincidents(status); create table disasterevents ( disaster event id uuid primary key default gen random uuid(), org id uuid not null references organizations(org id) on delete cascade, org affected event name varchar(255) not null, e g , 'site alpha flood event', 'datacenter fire dr' event type varchar(100) null, e g , 'natural disaster', 'fire', 'cyber attack', 'utility failure' declaration time utc timestamptz not null, end time utc timestamptz null, when normal operations resumed / dr ended activated bcdr plan id uuid null references bcdrplans(plan id) on delete set null, impact summary text, actions taken text, status varchar(30) check (status in ('declared', 'activerecovery', 'postrecovery', 'resolved')), declared by user id uuid null references users(user id) on delete set null, related ticket id bigint null references tickets(ticket id) on delete set null, created at timestamptz not null default current timestamp, updated at timestamptz ); comment on table disasterevents is 'tracks declared disaster events and the activation of bcdr plans (distinct from planned tests) '; \ stores information about printers connected locally or mapped from the network create table deviceprinters ( device printer id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, name varchar(255) not null, printer queue name device id string varchar(255) null, deviceid from win32 printer driver name varchar(255) null, manufacturer varchar(255) null, model varchar(255) null, port name varchar(255) null, is network printer boolean, is shared boolean, server name varchar(255) null, host if network printer share name varchar(255) null, share name if shared location text null, is default boolean, status varchar(50) null, e g , 'idle', 'printing', 'error' detected error state varchar(100) null, raw error state text/code error description text null, last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, name) name should be unique per device ); comment on table deviceprinters is 'stores details about local and mapped network printers discovered on a device '; comment on column deviceprinters device id string is 'deviceid property from win32 printer '; comment on column deviceprinters is network printer is 'indicates if this is a network mapped printer (vs locally connected) '; comment on column deviceprinters is shared is 'indicates if this local printer is shared on the network '; create index idx deviceprinters device id on deviceprinters(device id); \ stores network drives mapped by users on a device create table mappednetworkdrives ( mapped drive id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, user sid varchar(100) not null, sid of the user context the drive is mapped under drive letter char(2) not null, e g , 'z ' provider name text not null, unc path (e g , \\\server\share) display name varchar(255) null, friendly name if available file system varchar(50) null, e g , 'ntfs', 'fat32' (may not always be available) total size bytes bigint null, free space bytes bigint null, last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, user sid, drive letter) unique mapping per user per drive letter on a device ); comment on table mappednetworkdrives is 'stores network drives mapped within user sessions on a device '; comment on column mappednetworkdrives user sid is 'sid of the user for whom this drive mapping exists '; comment on column mappednetworkdrives provider name is 'the unc path of the mapped network share '; create index idx mappednetworkdrives device id on mappednetworkdrives(device id); create index idx mappednetworkdrives user sid on mappednetworkdrives(user sid); \ stores configuration for windows firewall profiles (domain, private, public) create table devicefirewallprofiles ( firewall profile id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, profile name varchar(20) not null check (profile name in ('domain', 'private', 'public')), is enabled boolean null, default inbound action varchar(10) null check (default inbound action in ('allow', 'block')), default outbound action varchar(10) null check (default outbound action in ('allow', 'block')), allow unicast response boolean null, notify on listen boolean null, notifications setting block all inbound boolean null, last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, profile name) ); comment on table devicefirewallprofiles is 'stores configuration settings for the different windows firewall profiles (domain, private, public) on a device '; create index idx devicefirewallprofiles device id on devicefirewallprofiles(device id); \ stores individual windows firewall rules create table devicefirewallrules ( firewall rule id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, rule name varchar(255) not null, group name varchar(255) null, profile names text\[] null, profiles this rule applies to ('domain', 'private', 'public', 'any') is enabled boolean null, direction varchar(10) null check (direction in ('inbound', 'outbound')), action varchar(10) null check (action in ('allow', 'block')), protocol varchar(50) null, e g , 'tcp', 'udp', 'icmpv4', 'any', protocol number local ports text null, e g , '80', '443', '1000 2000', 'rpc' remote ports text null, local addresses text null, e g , 'any', 'localsubnet', specific ips/cidrs remote addresses text null, program path text null, application path restriction service name varchar(255) null, service name restriction description text null, last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, rule name) assuming rule names are unique per device, adjust if needed ); comment on table devicefirewallrules is 'stores individual windows firewall rules discovered on a device '; create index idx devicefirewallrules device id on devicefirewallrules(device id); \ stores discovered local user accounts on a device create table localuseraccounts ( local user account id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, account name varchar(256) not null, full name varchar(255) null, description text null, sid varchar(100) not null, sid type varchar(30) null, e g , 'user', 'group', 'domain', 'alias' is disabled boolean null, is locked out boolean null, is local account boolean null, should always be true for this table is admin boolean null, indicates membership in local administrators group password changeable boolean null, password expires boolean null, password required boolean null, status varchar(20) null, e g , 'ok', 'degraded', 'error' from wmi status last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, sid) ); comment on table localuseraccounts is 'stores details about local user accounts discovered on a device '; comment on column localuseraccounts is admin is 'indicates if this local user is a member of the local administrators group '; create index idx localuseraccounts device id on localuseraccounts(device id); create index idx localuseraccounts sid on localuseraccounts(sid); \ stores discovered local groups on a device create table localgroups ( local group id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, group name varchar(256) not null, description text null, sid varchar(100) not null, sid type varchar(30) null, status varchar(20) null, last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, sid) ); comment on table localgroups is 'stores details about local groups discovered on a device '; create index idx localgroups device id on localgroups(device id); create index idx localgroups sid on localgroups(sid); \ stores membership of local groups create table localgroupmemberships ( local group membership id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, group sid varchar(100) not null, sid of the local group member sid varchar(100) not null, sid of the member (local user, domain user, domain group) member name varchar(256) null, name of the member (e g , 'builtin\\\administrators', 'domain\\\user') member domain varchar(255) null, member type varchar(30) null, from parsing sid or name last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, group sid, member sid) \ consider adding fks to localgroups(sid) and potentially adusers/adgroups(object sid) if sids are reliable cross domain ); comment on table localgroupmemberships is 'stores the membership of local groups on a device '; create index idx localgroupmemberships device id on localgroupmemberships(device id); create index idx localgroupmemberships group sid on localgroupmemberships(group sid); create index idx localgroupmemberships member sid on localgroupmemberships(member sid); create table localuserprofiles ( local user profile id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, account sid varchar(100) not null, fk conceptually links to localuseraccounts sid profile path text null, added path to the user profile directory size bytes bigint null, added calculated size of the profile directory status varchar(30) null check (status in ('ok', 'roaming', 'temporary', 'backup', 'mandatory', 'corrupt', 'unknown')), added profile status flags last used time utc timestamptz null, added timestamp when profile was last used/loaded last logon timestamptz null, from win32 networkloginprofile logon count integer null, from win32 networkloginprofile bad password count integer null, from win32 networkloginprofile password age seconds bigint null, from win32 networkloginprofile account expires timestamptz null, from win32 networkloginprofile last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, account sid) ); comment on table localuserprofiles is 'stores network login and profile status information (like last logon, password age, profile size, status, last used) associated with local user accounts '; comment on column localuserprofiles account sid is 'sid of the local user account this profile belongs to '; comment on column localuserprofiles status is 'status flags indicating profile type (roaming, temporary, mandatory) or state '; create index idx localuserprofiles device id on localuserprofiles(device id); create index idx localuserprofiles account sid on localuserprofiles(account sid); \ stores static ip routes configured on a device create table devicestaticroutes ( static route id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, destination cidr cidr not null, gateway ip inet not null, metric integer null, interface index integer null, interface name varchar(255) null, can be derived from index is persistent boolean null, last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, destination cidr, gateway ip, interface index) ); comment on table devicestaticroutes is 'stores static ip routes configured on a device '; create index idx devicestaticroutes device id on devicestaticroutes(device id); \ stores battery information for devices that have them (laptops) create table devicebattery ( device battery id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, caption varchar(255) null, e g , 'internal battery' description varchar(255) null, battery status enum varchar(50) null, e g , 'charging', 'discharging', 'fully charged', 'low' (mapped from codes) charge remaining percent integer null, estimated runtime minutes integer null, full charge capacity mwh integer null, design capacity mwh integer null, time on battery seconds bigint null, expected lifespan seconds bigint null, expectedlife from wmi time to full charge seconds bigint null, chemistry varchar(50) null, e g , 'li ion' health status varchar(50) null, general health if available last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, caption) assuming caption/id is unique per device ); comment on table devicebattery is 'stores status and health information for batteries in devices like laptops '; create index idx devicebattery device id on devicebattery(device id); \ stores specific registry values being monitored based on policy/configuration create table monitoredregistryvalues ( monitored reg id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, key path text not null, full path to the registry key value name varchar(255) not null, name of the value (@ for default) value data text null, value stored as text data type varchar(30) null, e g , 'reg sz', 'reg dword', 'reg binary' last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, key path, value name) ); comment on table monitoredregistryvalues is 'stores the current value of specific registry keys/values being monitored on devices '; create index idx monitoredregistryvalues device id on monitoredregistryvalues(device id); \ stores applications configured to run at startup create table devicestartupitems ( startup item id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, item name varchar(255) not null, publisher varchar(255) null, status varchar(20) null, e g , 'enabled', 'disabled' startup type varchar(30) null, e g , 'registryrun', 'startupfolder', 'taskscheduler' command line text null, is running now boolean null, process id integer null, last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, item name, startup type, command line) best guess at uniqueness ); comment on table devicestartupitems is 'stores information about applications configured to run at system startup '; create index idx devicestartupitems device id on devicestartupitems(device id); \ stores active user sessions on a device create table devicesessions ( device session id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, user name varchar(255) not null, user domain varchar(255) null, winstation name varchar(100) null, e g , 'console', 'rdp tcp#0' session state varchar(50) null, e g , 'active', 'disconnected' logon time utc timestamptz null, start time of this specific session last seen time timestamptz not null default current timestamp, when this session was last detected as active/disconnected created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, winstation name, user name, user domain) combination should be unique for active sessions ); comment on table devicesessions is 'stores information about active user sessions (console, rdp, etc ) on a device '; create index idx devicesessions device id on devicesessions(device id); \ stores os upgrade/feature update history create table osupgradehistory ( os upgrade id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, product name varchar(255) null, e g , 'windows 11 enterprise' edition varchar(100) null, release id varchar(50) null, e g , '23h2' install type varchar(50) null, e g , 'feature update', 'clean install' system root text null, install timestamp utc timestamptz not null, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, install timestamp utc) should be unique install event per device ); comment on table osupgradehistory is 'stores history of major os upgrades or feature updates applied to a device '; create index idx osupgradehistory device id on osupgradehistory(device id); create table devicediskencryptionstatus ( disk encryption id bigserial primary key, logical disk id uuid not null references devicelogicaldisks(disk id) on delete cascade, device id uuid not null references devices(device id) on delete cascade, denormalized for easier query encryption method varchar(30) not null, e g , 'bitlocker', 'filevault', 'luks', 'veracrypt' encryption status varchar(30) not null check (encryption status in ('fullyencrypted', 'fullydecrypted', 'encryptioninprogress', 'decryptioninprogress', 'encryptionpaused', 'decryptionpaused', 'locked', 'notapplicable', 'unknown')), protection status varchar(20) null check (protection status in ('on', 'off', 'unknown')), bitlocker specific protection suspend status percent encrypted integer null check (percent encrypted >= 0 and percent encrypted <= 100), lock status varchar(20) null check (lock status in ('locked', 'unlocked', 'unknown')), is the volume currently locked? protector types text\[] null, e g , \['tpm', 'password', 'recoverykey', 'startupkey'] recovery key encrypted bytea null, encrypted bitlocker recovery key (high risk see comment) last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique(logical disk id) assuming one encryption status per logical disk ); comment on table devicediskencryptionstatus is 'tracks the encryption status (bitlocker, filevault, luks, etc ) of logical disks includes encrypted recovery key storage (use with extreme caution) '; comment on column devicediskencryptionstatus logical disk id is 'links to the specific logical disk (volume) record '; comment on column devicediskencryptionstatus protector types is 'list of key protectors enabled for this volume (e g , tpm, password) '; comment on column devicediskencryptionstatus recovery key encrypted is '\[high risk] encrypted bitlocker recovery key requires robust application level encryption/decryption with unique keys per tenant/device, strict role based access control (rbac) within the application to limit decryption capability, and comprehensive, immutable audit logging for all decryption attempts (successful or failed) prefer using ad/azure ad/vault storage instead '; \ add relevant indexes create index idx devicediskencryption device id on devicediskencryptionstatus(device id); create index idx devicediskencryption status on devicediskencryptionstatus(encryption status); create index idx devicediskencryption method on devicediskencryptionstatus(encryption method); \ stores detailed microsoft defender configuration settings for a device create table devicedefenderconfiguration ( defender config id bigserial primary key, device id uuid not null references devices(device id) on delete cascade unique, one config record per device \ general / ui ui lockdown enabled boolean null, maps to uilockdown force device control enabled boolean null, placeholder if needed later \ real time protection is realtime monitoring enabled boolean null, maps to disablerealtimemonitoring (inverted) is behavior monitoring enabled boolean null, maps to disablebehaviormonitoring (inverted) is ioav protection enabled boolean null, maps to disableioavprotection (inverted) (scan downloads/attachments) is script scanning enabled boolean null, maps to disablescriptscanning (inverted) scan on realtime access enabled boolean null, maps to disablescanonrealtimeaccess (inverted) (ntfs scanning related) \ cloud protection cloud block level varchar(30) null, maps to mapsreporting basic/advanced cloud extended timeout integer null, maps to cloudextendedtimeout is cloud protection enabled boolean null, maps to mapsreporting > 0 submit samples consent varchar(30) null, maps to submitsamplesconsent none/safe/malware/all is pua protection enabled varchar(30) null, maps to puaprotection off/on/audit \ scans scan only if idle enabled boolean null, scan email enabled boolean null, maps to disableemailscanning (inverted) scan removable drives enabled boolean null, maps to disableremovabledrivescanning (inverted) scan restore points enabled boolean null, maps to disablerestorepoint (inverted) scan network files enabled boolean null, maps to disablescanningnetworkfiles (inverted) scan archives enabled boolean null, maps to disablearchivescanning (inverted) check for signatures before scan boolean null, catchup full scan enabled boolean null, maps to disablecatchupfullscan (inverted) catchup quick scan enabled boolean null, maps to disablecatchupquickscan (inverted) avg cpu load factor integer null, maps to scanavgcpuloadfactor (max scan cpu %) quarantine purge days integer null, maps to quarantinepurgeitemsafterdelay scheduled scan day varchar(20) null, maps to scheduledscanday everyday/specificday/never scheduled scan time of day time null, maps to scheduledscantime (requires conversion) scheduled scan type varchar(20) null, maps to scanparameters quick/full randomize schedule task times boolean null, \ threat actions (low, moderate, high, severe maps to threatiddefaultaction ids where id maps to severity) low threat action varchar(30) null, clean/quarantine/remove/allow/userdefined/block moderate threat action varchar(30) null, high threat action varchar(30) null, severe threat action varchar(30) null, unknown threat action varchar(30) null, placeholder, may not map directly \ asr / advanced (maps to attacksurfacereductionrules ids and attacksurfacereductiononlyexclusions) asr rules state jsonb null, store state per rule guid, e g , {"guid1" "enabled", "guid2" "audit"} network protection level varchar(30) null, maps to enablenetworkprotection off/on/audit controlled folder access state varchar(30) null, maps to enablecontrolledfolderaccess off/on/audit controlled folder access protected folders text\[] null, controlled folder access allowed apps text\[] null, \ exclusions excluded processes text\[] null, maps to excludedprocesses excluded paths text\[] null, maps to excludedpaths excluded extensions text\[] null, maps to excludedextensions \ updates signature update interval hours integer null, maps to signatureupdateinterval \ other relevant settings add as identified from get mppreference last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); comment on table devicedefenderconfiguration is 'stores detailed configuration settings for microsoft defender antivirus on a device, primarily collected via get mppreference '; create index idx devicedefenderconfig device id on devicedefenderconfiguration(device id); \ stores hardware sensor readings (temperature, fan speed) create table devicesensorreadings ( sensor reading id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, sensor type varchar(30) not null check (sensor type in ('temperature', 'fanspeed', 'voltage', 'other')), sensor name varchar(255) not null, identifier from os/hardware (e g , 'cpu core 0', 'gpu die', 'thermal zone 0', 'chassis fan 1') reading value real null, the numeric value (e g , degrees c for temp, rpm for fan) reading unit varchar(10) null, e g , 'c', 'rpm', 'v' status varchar(30) null, optional status if provided by sensor (e g , 'ok', 'warning', 'critical') reading timestamp utc timestamptz not null default current timestamp, when the reading was taken created at timestamptz not null default current timestamp, \ add index on device id, sensor name, reading timestamp utc for time series style queries unique (device id, sensor name, reading timestamp utc) ensure unique reading per sensor at a given time ); comment on table devicesensorreadings is 'stores periodic hardware sensor readings like temperature and fan speed '; create index idx devicesensorreadings device time on devicesensorreadings(device id, reading timestamp utc desc); \ stores details about installed device drivers create table devicedrivers ( driver id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, device name varchar(255) null, friendly name of the hardware device using the driver pnp device id varchar(512) null, plug and play device id from win32 pnpsigneddriver driver provider varchar(255) null, manufacturer/provider of the driver driver version varchar(100) null, driver date date null, is signed boolean null, inf name varchar(255) null, inf file name (windows) driver path text null, path to driver file(s) if available last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, pnp device id, driver version) approximate uniqueness for windows \ uniqueness might be harder cross platform, potentially base on device identifier + driver path/name ); comment on table devicedrivers is 'stores information about installed device drivers on an endpoint '; create index idx devicedrivers device id on devicedrivers(device id); create index idx devicedrivers pnp id on devicedrivers(pnp device id); \ stores time series data about active application/window usage per user session create table useractivitylog ( activity log id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, user session identifier text not null, identifier linking to the specific user session (e g , user sid + session id) application name varchar(255) null, name of the primary application (e g , 'google chrome') process name varchar(255) null, name of the executable (e g , 'chrome exe') window title text null, title text of the foreground window url text null, url if detected from a supported browser (best effort) start time utc timestamptz not null, when this activity period started end time utc timestamptz not null, when this activity period ended (focus change or idle) duration seconds integer not null check (duration seconds >= 0), calculated duration recorded at timestamptz not null default current timestamp when the server recorded this batch ); comment on table useractivitylog is 'stores recorded foreground application usage periods for tracked devices/users '; comment on column useractivitylog user session identifier is 'identifies the specific user session this activity belongs to '; comment on column useractivitylog url is 'url captured from supported browsers during the activity period (best effort) '; \ add indexes appropriate for time series querying and aggregation create index idx useractivitylog device time on useractivitylog(device id, start time utc desc); create index idx useractivitylog user session on useractivitylog(user session identifier); create index idx useractivitylog app name on useractivitylog(application name); \ stores information about detected web browsers on a device create table devicebrowsers ( device browser id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, browser name varchar(100) not null, e g , 'google chrome', 'microsoft edge', 'mozilla firefox', 'safari' version varchar(50) null, install path text null, is default boolean null, last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, browser name, install path) assumes name+path is unique per device ); comment on table devicebrowsers is 'stores basic identifying information about web browsers installed on a device '; create index idx devicebrowsers device id on devicebrowsers(device id); \ stores details about installed browser extensions create table browserextensions ( browser extension id bigserial primary key, device browser id bigint not null references devicebrowsers(device browser id) on delete cascade, link to the specific browser instance device id uuid not null references devices(device id) on delete cascade, denormalized for easier querying user context varchar(100) not null, user sid or identifier ('all users', 'system') this extension is installed for extension id varchar(255) not null, unique id from the browser/store name varchar(255) null, version varchar(50) null, is enabled boolean null, install path text null, path within profile if found last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device browser id, user context, extension id) ); comment on table browserextensions is 'stores details about extensions installed for specific browsers and user contexts on a device '; create index idx browserextensions device id on browserextensions(device id); create index idx browserextensions device browser id on browserextensions(device browser id); create index idx browserextensions extension id on browserextensions(extension id); \ stores key browser configuration settings, focusing on policy derived values create table browserconfiguration ( browser config id bigserial primary key, device browser id bigint not null references devicebrowsers(device browser id) on delete cascade, link to the specific browser instance device id uuid not null references devices(device id) on delete cascade, denormalized user context varchar(100) not null, user sid or identifier ('machinepolicy', 'recommended') this setting applies to setting name varchar(255) not null, name of the setting (e g , 'safebrowseenabled', 'passwordmanagerenabled', 'autoupdatecheckenabled') setting value text null, value of the setting source varchar(30) null check (source in ('policy', 'mdm', 'userpreference', 'default')), how the setting was determined last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device browser id, user context, setting name) ); comment on table browserconfiguration is 'stores key configuration settings discovered for specific browsers/user contexts, prioritizing policy sources '; create index idx browserconfiguration device id on browserconfiguration(device id); create index idx browserconfiguration device browser id on browserconfiguration(device browser id); create index idx browserconfiguration setting name on browserconfiguration(setting name); \ stores detailed os audit policy settings per subcategory create table detailedauditpolicysettings ( audit policy setting id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, policy category varchar(100) not null, e g , 'system', 'logon/logoff', 'object access' policy subcategory varchar(255) not null, e g , 'security state change', 'logon', 'file system' auditing flags varchar(20) not null check (auditing flags in ('none', 'success', 'failure', 'success and failure', 'unknown')), effective setting last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (device id, policy category, policy subcategory) ); comment on table detailedauditpolicysettings is 'stores granular operating system audit policy settings (success/failure) per subcategory '; create index idx detailedauditpolicy device id on detailedauditpolicysettings(device id); \ track remote control sessions create table remotecontrolsessions ( remote control session id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, technician user id bigint null, link to user initiating the session if available start time utc timestamptz not null, end time utc timestamptz null, null if session is still active session status varchar(20) not null check (session status in ('requested', 'connected', 'disconnected', 'failed')), remote control tool varchar(100) null, e g , 'teamviewer', 'screenconnect' connection details text null, tool specific connection info (e g , session id, invitation code) recorded at timestamptz not null default current timestamp ); comment on table remotecontrolsessions is 'tracks remote control sessions initiated to managed devices '; create index idx remotecontrolsessions device id on remotecontrolsessions(device id); create index idx remotecontrolsessions technician on remotecontrolsessions(technician user id); \ manages software packages for deployment create table softwarepackages ( software package id bigserial primary key, package name varchar(255) not null, e g , 'google chrome', '7 zip' version varchar(50) null, architecture varchar(20) null, 'x86', 'x64', 'arm64' package type varchar(20) not null check (package type in ('msi', 'pkg', 'exe', 'deb', 'rpm', 'script')), package location text null, unc path or url to the package created at timestamptz not null default current timestamp, updated at timestamptz, auto updated unique (package name, version, architecture, package type) ); comment on table softwarepackages is 'stores details about available software packages for deployment to devices '; \ defines deployment jobs for software packages create table softwaredeploymentjobs ( deployment job id bigserial primary key, software package id bigint not null references softwarepackages(software package id) on delete cascade, job name varchar(255) null, target type varchar(20) not null check (target type in ('device', 'devicegroup', 'policy')), target id uuid not null, device id, device group id, or policy id schedule type varchar(20) null check (schedule type in ('immediate', 'scheduled', 'recurring')), scheduled time utc timestamptz null, for 'scheduled' recurring cron expression varchar(255) null, for 'recurring' arguments text null, command line arguments for scripts/installers timeout seconds integer null, max time to wait for completion created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); comment on table softwaredeploymentjobs is 'defines software deployment jobs, targeting devices/groups/policies '; create index idx softwaredeploymentjobs package on softwaredeploymentjobs(software package id); \ tracks the status of a specific software deployment job on a device create table devicedeploymentstatus ( device deployment status id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, deployment job id bigint not null references softwaredeploymentjobs(deployment job id) on delete cascade, status varchar(20) not null check (status in ('pending', 'downloading', 'installing', 'success', 'failed', 'timeout')), start time utc timestamptz null, when the deployment started end time utc timestamptz null, when the deployment ended (or null if still running) exit code integer null, for script executions log output text null, capture logs from the deployment process recorded at timestamptz not null default current timestamp ); comment on table devicedeploymentstatus is 'tracks the status of software deployments on individual devices '; create index idx devicedeploystatus device id on devicedeploymentstatus(device id); create index idx devicedeploystatus job id on devicedeploymentstatus(deployment job id); \ stores crash dump data from devices create table devicecrashdumps ( device crash dump id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, crash time utc timestamptz not null, bugcheck code varchar(50) null, windows bsod code crashing driver varchar(255) null, dump file path text null, path to the uploaded dump file recorded at timestamptz not null default current timestamp, notes text null any additional notes or analysis ); comment on table devicecrashdumps is 'stores details of system crashes (bsods) reported by devices '; create index idx devicecrashdumps device id on devicecrashdumps(device id); \ stores granular, potentially high volume application usage data create table softwareusagelog ( software usage log id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, application name varchar(255) not null, version varchar(50) null, start time utc timestamptz not null, end time utc timestamptz null, null if still running duration seconds integer null, calculated duration user context varchar(100) null, user account is foreground boolean null, was the application in the foreground? recorded at timestamptz not null default current timestamp ); comment on table softwareusagelog is 'tracks time series usage of installed software on devices '; create index idx softwareusagelog device id on softwareusagelog(device id); create index idx softwareusagelog app name on softwareusagelog(application name); \ logs usb device connection/disconnection events create table deviceusbhistory ( device usb history id bigserial primary key, device id uuid not null references devices(device id) on delete cascade, event time utc timestamptz not null, event type varchar(20) not null check (event type in ('connect', 'disconnect')), device name varchar(255) null, from the os, if available vendor id varchar(10) null, product id varchar(10) null, serial number varchar(255) null, user context varchar(100) null, user account recorded at timestamptz not null default current timestamp ); comment on table deviceusbhistory is 'logs connection/disconnection events for usb devices attached to tracked devices '; create index idx deviceusbhistory device id on deviceusbhistory(device id); \ stores configuration settings for discovered hypervisor hosts create table hypervisorhostconfiguration ( device id uuid primary key references devices(device id) on delete cascade, links 1 to 1 with the host device hypervisor type varchar(30) not null check (hypervisor type in ('hyper v', 'vmware workstation', 'vmware fusion', 'virtualbox', 'kvm/libvirt', 'parallels', 'other', 'unknown')), type detected by agent os version text null, host os version string default vm config path text null, default path for vm definitions default vhd path text null, default path for virtual disks numa spanning enabled boolean null, \ hyper v specific settings moved to jsonb \ other hypervisor specific settings can also go here configuration details jsonb null, store hypervisor specific details (live migration, replication, enhanced session mode, vswitch settings etc ) error state text null, errors during collection for this host last seen time timestamptz not null default current timestamp, created at timestamptz not null default current timestamp, updated at timestamptz auto updated ); comment on table hypervisorhostconfiguration is 'stores configuration settings for discovered hypervisor hosts (hyper v, vmware workstation/fusion, virtualbox, kvm, etc ) v1 agent populates only for hyper v '; comment on column hypervisorhostconfiguration hypervisor type is 'the type of hypervisor software detected running directly on the host os '; comment on column hypervisorhostconfiguration configuration details is 'jsonb field storing hypervisor specific configuration details not covered by common fields (e g , hyper v live migration/replication settings, vmware network types) '; \ stores details about virtual switches configured on a hypervisor host create table hostvirtualswitches ( host vswitch id bigserial primary key, host device id uuid not null references devices(device id) on delete cascade, link to the host device switch name varchar(255) not null, switch type varchar(30) null, generic types ('external', 'internal', 'private') or hypervisor specific values notes text null, bound adapter name varchar(255) null, physical nic (external) or host vnic (internal/host only) details jsonb null, store hypervisor specific properties (e g , vlan id for internal, iov settings, nat config) last seen time timestamptz not null default current timestamp, unique (host device id, switch name) ); comment on table hostvirtualswitches is 'stores details about virtual switches configured on a hypervisor host '; comment on column hostvirtualswitches details is 'jsonb field for hypervisor specific virtual switch settings (vlan id, iov, nat config, etc ) '; create index idx hostvswitches host on hostvirtualswitches(host device id); \ stores inventory of virtual machines running on a hypervisor host create table virtualmachines ( vm inventory id bigserial primary key, host device id uuid not null references devices(device id) on delete cascade, link to the host device hypervisor type varchar(30) not null, denormalized from host for easier filtering vm identifier varchar(255) not null, hypervisor's unique id (e g , hyper v guid, vmware vmx path hash, libvirt uuid) vm name varchar(255) null, user friendly name state varchar(50) null, e g , 'running', 'off', 'paused', 'saved', 'starting', 'stopping' cpu count integer null, number of vcpus assigned memory assigned mb integer null, uptime seconds bigint null, guest os text null, os name reported by guest tools/kvp network interfaces jsonb null, array \[{ "mac" " ", "ip v4" \[" "], "ip v6" \[" "], "switch name" " " }] storage volumes jsonb null, array \[{ "path" " ", "type" "dynamic/fixed/diff", "configured size gb" x x, "actual size gb" y y, "parent path" " ", "access status" "ok/error " }] notes text null, creation time utc timestamptz null, details jsonb null, store hypervisor specific vm details (health state, op status, dynamic mem config, cpu limits, kvp raw data, integration svc version etc ) last seen time timestamptz not null default current timestamp, when this vm record was last updated by inventory unique (host device id, vm identifier) ); comment on table virtualmachines is 'stores inventory details for each virtual machine discovered on a hypervisor host '; comment on column virtualmachines vm identifier is 'unique identifier for the vm within its hypervisor (e g , hyper v guid, libvirt uuid) '; comment on column virtualmachines network interfaces is 'jsonb array storing details of assigned virtual nics '; comment on column virtualmachines storage volumes is 'jsonb array storing details of attached virtual disks '; comment on column virtualmachines details is 'jsonb field for hypervisor specific vm details not covered by main columns (e g , health states, dynamic memory, cpu limits, integration services info) '; create index idx vms host on virtualmachines(host device id); create index idx vms identifier on virtualmachines(vm identifier); create index idx vms name on virtualmachines(vm name);