+-- Slices at each site
+CREATE VIEW site_slices AS
+SELECT site_id,
+array_accum(slice_id) AS slice_ids
+FROM slices
+WHERE is_deleted is false
+GROUP BY site_id;
+
+-- Slice membership
+CREATE TABLE slice_person (
+ slice_id integer REFERENCES slices NOT NULL, -- Slice identifier
+ person_id integer REFERENCES persons NOT NULL, -- Account identifier
+ PRIMARY KEY (slice_id, person_id)
+) WITH OIDS;
+CREATE INDEX slice_person_slice_id_idx ON slice_person (slice_id);
+CREATE INDEX slice_person_person_id_idx ON slice_person (person_id);
+
+-- Members of the slice
+CREATE VIEW slice_persons AS
+SELECT slice_id,
+array_accum(person_id) AS person_ids
+FROM slice_person
+GROUP BY slice_id;
+
+-- Slices of which each person is a member
+CREATE VIEW person_slices AS
+SELECT person_id,
+array_accum(slice_id) AS slice_ids
+FROM slice_person
+GROUP BY person_id;
+
+--------------------------------------------------------------------------------
+-- Slice attributes
+--------------------------------------------------------------------------------
+
+-- Slice attribute types
+CREATE TABLE slice_attribute_types (
+ attribute_type_id serial PRIMARY KEY, -- Attribute type identifier
+ name text UNIQUE NOT NULL, -- Attribute name
+ description text, -- Attribute description
+ min_role_id integer REFERENCES roles DEFAULT 10 -- If set, minimum (least powerful) role that can set or change this attribute
+) WITH OIDS;
+
+-- Slice/sliver attributes
+CREATE TABLE slice_attribute (
+ slice_attribute_id serial PRIMARY KEY, -- Slice attribute identifier
+ slice_id integer REFERENCES slices NOT NULL, -- Slice identifier
+ node_id integer REFERENCES nodes, -- Sliver attribute if set
+ attribute_type_id integer REFERENCES slice_attribute_types NOT NULL, -- Attribute type identifier
+ value text
+) WITH OIDS;
+CREATE INDEX slice_attribute_slice_id_idx ON slice_attribute (slice_id);
+CREATE INDEX slice_attribute_node_id_idx ON slice_attribute (node_id);
+
+CREATE VIEW slice_attributes AS
+SELECT slice_id,
+array_accum(slice_attribute_id) AS slice_attribute_ids
+FROM slice_attribute
+GROUP BY slice_id;
+
+--------------------------------------------------------------------------------
+-- Peers
+--------------------------------------------------------------------------------
+
+-- Peers
+CREATE TABLE peers (
+ peer_id serial PRIMARY KEY, -- Peer identifier
+ peername text NOT NULL, -- Peer name
+ peer_url text NOT NULL, -- (HTTPS) URL of the peer PLCAPI interface
+ cacert text, -- (SSL) Public certificate of peer API server
+ key text, -- (GPG) Public key used for authentication
+ deleted boolean NOT NULL DEFAULT false
+) WITH OIDS;
+CREATE INDEX peers_peername_idx ON peers (peername) WHERE deleted IS false;
+
+-- Objects at each peer
+CREATE TABLE peer_site (
+ site_id integer REFERENCES sites PRIMARY KEY, -- Local site identifier
+ peer_id integer REFERENCES peers NOT NULL, -- Peer identifier
+ peer_site_id integer NOT NULL, -- Foreign site identifier at peer
+ UNIQUE (peer_id, peer_site_id) -- The same foreign site should not be cached twice
+) WITH OIDS;
+CREATE INDEX peer_site_peer_id_idx ON peers (peer_id);
+
+CREATE VIEW peer_sites AS
+SELECT peer_id,
+array_accum(site_id) AS site_ids,
+array_accum(peer_site_id) AS peer_site_ids
+FROM peer_site
+GROUP BY peer_id;
+
+CREATE TABLE peer_person (
+ person_id integer REFERENCES persons PRIMARY KEY, -- Local user identifier
+ peer_id integer REFERENCES peers NOT NULL, -- Peer identifier
+ peer_person_id integer NOT NULL, -- Foreign user identifier at peer
+ UNIQUE (peer_id, peer_person_id) -- The same foreign user should not be cached twice
+) WITH OIDS;
+CREATE INDEX peer_person_peer_id_idx ON peer_person (peer_id);
+
+CREATE VIEW peer_persons AS
+SELECT peer_id,
+array_accum(person_id) AS person_ids,
+array_accum(peer_person_id) AS peer_person_ids
+FROM peer_person
+GROUP BY peer_id;
+
+CREATE TABLE peer_key (
+ key_id integer REFERENCES keys PRIMARY KEY, -- Local key identifier
+ peer_id integer REFERENCES peers NOT NULL, -- Peer identifier
+ peer_key_id integer NOT NULL, -- Foreign key identifier at peer
+ UNIQUE (peer_id, peer_key_id) -- The same foreign key should not be cached twice
+) WITH OIDS;
+CREATE INDEX peer_key_peer_id_idx ON peer_key (peer_id);
+
+CREATE VIEW peer_keys AS
+SELECT peer_id,
+array_accum(key_id) AS key_ids,
+array_accum(peer_key_id) AS peer_key_ids
+FROM peer_key
+GROUP BY peer_id;
+
+CREATE TABLE peer_node (
+ node_id integer REFERENCES nodes PRIMARY KEY, -- Local node identifier
+ peer_id integer REFERENCES peers NOT NULL, -- Peer identifier
+ peer_node_id integer NOT NULL, -- Foreign node identifier
+ UNIQUE (peer_id, peer_node_id) -- The same foreign node should not be cached twice
+) WITH OIDS;
+CREATE INDEX peer_node_peer_id_idx ON peer_node (peer_id);
+
+CREATE VIEW peer_nodes AS
+SELECT peer_id,
+array_accum(node_id) AS node_ids,
+array_accum(peer_node_id) AS peer_node_ids
+FROM peer_node
+GROUP BY peer_id;
+
+CREATE TABLE peer_slice (
+ slice_id integer REFERENCES slices PRIMARY KEY, -- Local slice identifier
+ peer_id integer REFERENCES peers NOT NULL, -- Peer identifier
+ peer_slice_id integer NOT NULL, -- Slice identifier at peer
+ UNIQUE (peer_id, peer_slice_id) -- The same foreign slice should not be cached twice
+) WITH OIDS;
+CREATE INDEX peer_slice_peer_id_idx ON peer_slice (peer_id);
+
+CREATE VIEW peer_slices AS
+SELECT peer_id,
+array_accum(slice_id) AS slice_ids,
+array_accum(peer_slice_id) AS peer_slice_ids
+FROM peer_slice
+GROUP BY peer_id;
+
+--------------------------------------------------------------------------------
+-- Authenticated sessions
+--------------------------------------------------------------------------------
+
+-- Authenticated sessions
+CREATE TABLE sessions (
+ session_id text PRIMARY KEY, -- Session identifier
+ expires timestamp without time zone
+) WITH OIDS;
+
+-- People can have multiple sessions
+CREATE TABLE person_session (
+ person_id integer REFERENCES persons NOT NULL, -- Account identifier
+ session_id text REFERENCES sessions NOT NULL, -- Session identifier
+ PRIMARY KEY (person_id, session_id),
+ UNIQUE (session_id) -- Sessions are unique
+) WITH OIDS;
+CREATE INDEX person_session_person_id_idx ON person_session (person_id);
+
+-- Nodes can have only one session
+CREATE TABLE node_session (
+ node_id integer REFERENCES nodes NOT NULL, -- Node identifier
+ session_id text REFERENCES sessions NOT NULL, -- Session identifier
+ UNIQUE (node_id), -- Nodes can have only one session
+ UNIQUE (session_id) -- Sessions are unique
+) WITH OIDS;
+
+--------------------------------------------------------------------------------
+-- Message templates
+--------------------------------------------------------------------------------
+
+CREATE TABLE messages (
+ message_id text PRIMARY KEY, -- Message name
+ subject text, -- Message summary
+ template text, -- Message template
+ enabled bool NOT NULL DEFAULT true -- Whether message is enabled
+) WITH OIDS;
+
+--------------------------------------------------------------------------------
+-- Events
+--------------------------------------------------------------------------------
+
+-- Events
+CREATE TABLE events (
+ event_id serial PRIMARY KEY, -- Event identifier
+ person_id integer REFERENCES persons, -- Person responsible for event, if any
+ node_id integer REFERENCES nodes, -- Node responsible for event, if any
+ fault_code integer NOT NULL DEFAULT 0, -- Did this event result in error
+ call_name text NOT NULL, -- Call responsible for this event
+ call text NOT NULL, -- Call responsible for this event, including parameters
+ message text, -- High level description of this event
+ runtime float DEFAULT 0, -- Event run time
+ time timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP -- Event timestamp
+) WITH OIDS;
+
+-- Database object(s) that may have been affected by a particular event
+CREATE TABLE event_object (
+ event_id integer REFERENCES events NOT NULL, -- Event identifier
+ object_id integer NOT NULL -- Object identifier
+) WITH OIDS;
+CREATE INDEX event_object_event_id_idx ON event_object (event_id);
+CREATE INDEX event_object_object_id_idx ON event_object (object_id);
+
+CREATE VIEW event_objects AS
+SELECT event_id,
+array_accum(object_id) AS object_ids
+FROM event_object
+GROUP BY event_id;
+