parsav  Check-in [050ce7d4fc]

Overview
Comment:iterating
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 050ce7d4fce81f6c7df4c14c22e80eba11ac3027ac24a114ee36e0cc77272099
User & Date: lexi on 2021-01-25 14:38:36
Other Links: manifest | tags
Context
2021-01-28
00:51
wrote mimelib, continued iterating on litepub support; tweets can now be imported into honk check-in: c774e2c5a9 user: lexi tags: trunk
2021-01-25
14:38
iterating check-in: 050ce7d4fc user: lexi tags: trunk
13:24
finish up LP support for user outbox views check-in: 3f080eded4 user: lexi tags: trunk
Changes

Modified backend/pgsql.t from [040ddf2134] to [18ea7fb9d0].

901
902
903
904
905
906
907
908








909
910
911
912
913
914
915
...
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
			buf[3] = sz
			for j=0,sz do buf[4 + j] = i.v6[j] end -- 😬
			return buf
		end
	end;
}

local sqlvars = {}








for i, n in ipairs(lib.store.noticetype.members) do
	sqlvars['notice:' .. n] = lib.store.noticetype[n]:asvalue()
end

for i, n in ipairs(lib.store.relation.members) do
	sqlvars['rel:' .. n] = lib.store.relation.idvmap[n]
end
................................................................................
				var ipbuf: int8[20]
				;[pqt[lib.store.inet](false)]([args[i]], [&uint8](&ipbuf))
			in &ipbuf[0] end
			dumpers[#dumpers+1] = `lib.io.fmt([tostring(i)..'. got inet\n'])
		elseif ty.ptr_basetype == int8 or ty.ptr_basetype == uint8 then
			counters[i] = `[args[i]].ct
			casts[i] = `[&int8]([args[i]].ptr)
			dumpers[#dumpers+1] = `lib.io.fmt([tostring(i)..'. got ptr %llu %.*s\n'], [args[i]].ct, [args[i]].ct, [args[i]].ptr)
		elseif ty.ptr_basetype == bool then
			counters[i] = `1
			casts[i] = `[&int8]([args[i]].ptr)
			-- dumpers[#dumpers+1] = `lib.io.fmt([tostring(i)..'. got bool = %hhu\n'], @[args[i]].ptr)
		elseif ty:isintegral() then
			counters[i] = ty.bytes
			casts[i] = `[&int8](&[args[i]])







|
>
>
>
>
>
>
>
>







 







|







901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
...
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
			buf[3] = sz
			for j=0,sz do buf[4 + j] = i.v6[j] end -- 😬
			return buf
		end
	end;
}

local sqlvars = {
 -- unforunately necessary to generate IDs that fill the whole
 -- address space (due to floating-point shenanigans); there's
 -- just no good way of doing this with SQL
	['def:uniq'] = [[bigint primary key default (
		 (random() * ((1::bigint << 32) - 1)    )::bigint << 32 | 
		 (random() * ((1::bigint << 32) - 2) + 1)::bigint
	)]]
}
for i, n in ipairs(lib.store.noticetype.members) do
	sqlvars['notice:' .. n] = lib.store.noticetype[n]:asvalue()
end

for i, n in ipairs(lib.store.relation.members) do
	sqlvars['rel:' .. n] = lib.store.relation.idvmap[n]
end
................................................................................
				var ipbuf: int8[20]
				;[pqt[lib.store.inet](false)]([args[i]], [&uint8](&ipbuf))
			in &ipbuf[0] end
			dumpers[#dumpers+1] = `lib.io.fmt([tostring(i)..'. got inet\n'])
		elseif ty.ptr_basetype == int8 or ty.ptr_basetype == uint8 then
			counters[i] = `[args[i]].ct
			casts[i] = `[&int8]([args[i]].ptr)
			dumpers[#dumpers+1] = `lib.io.fmt([tostring(i)..'. got ptr %p %.*s\n'], [args[i]].ct, [args[i]].ct, [args[i]].ptr)
		elseif ty.ptr_basetype == bool then
			counters[i] = `1
			casts[i] = `[&int8]([args[i]].ptr)
			-- dumpers[#dumpers+1] = `lib.io.fmt([tostring(i)..'. got bool = %hhu\n'], @[args[i]].ptr)
		elseif ty:isintegral() then
			counters[i] = ty.bytes
			casts[i] = `[&int8](&[args[i]])

Modified backend/schema/pgsql.sql from [daac991db5] to [4c18dab250].

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
..
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
..
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
...
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
...
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
--	('policy-security',:'secmode'),
--	('policy-self-register',:'regpol'),
--	('master',:'admin'),

-- note that valid ids should always > 0, as 0 is reserved for null
-- on the client side, vastly simplifying code
create table parsav_servers (
	id     bigint primary key default (1+random()*(2^63-1))::bigint,
	domain text not null unique,
	key    bytea,
	knownsince bigint,
	parsav boolean -- whether to use parsav protocol extensions
);
comment on table parsav_servers is
'all servers known to the parsav instance. the local server (including its private key) is stored in row (id = 0)';

create table parsav_actors (
	id        bigint primary key default (1+random()*(2^63-1))::bigint,
	nym       text,
	handle    text not null, -- nym [@handle@origin] 
	origin    bigint references parsav_servers(id)
		on delete cascade, -- null origin = local actor
	knownsince bigint not null,
	bio       text,
	avatarid  bigint, -- artifact id, null if remote
................................................................................
	primary key (key,actor)
);
create index on parsav_rights (actor);
comment on table parsav_rights is
'a backward-compatible list of every non-default privilege or deprivilege granted to a local user';

create table parsav_posts (
	id         bigint primary key default (1+random()*(2^63-1))::bigint,
	author     bigint references parsav_actors(id) on delete cascade,
	subject    text,
	acl        text not null default 'all', -- just store the script raw 🤷
	body       text,
	posted     bigint not null,
	discovered bigint not null,
	chgcount   integer not null default 0,
................................................................................

	primary key (relator, relatee, kind)
);
comment on table parsav_rels is
'all relationships, positive and negative, between local users and other users; kind is a version-specific integer mapping to a type-of-relationship enum in store.t';

create table parsav_acts (
	id      bigint primary key default (1+random()*(2^63-1))::bigint,
	kind    text not null, -- like, rt, react, so on
	time    bigint not null,
	actor   bigint references parsav_actors(id) on delete cascade,
	subject bigint, -- may be post or act, depending on kind
	body	text -- emoji, if react; complaint, if report
);
create index on parsav_acts (subject);
................................................................................
create index on parsav_acts (actor);
create index on parsav_acts (time);
comment on table parsav_acts is
'every simple action taken on a tweet by an actor, including likes, rts, reacts, and reports';

create table parsav_log (
	-- accesses are tracked for security & sending delete acts
	id    bigint primary key default (1+random()*(2^63-1))::bigint,
	time  bigint not null,
	actor bigint references parsav_actors(id)
		on delete cascade,
	post  bigint not null
);
comment on table parsav_log is
'a log of accesses from foreign servers, tracking which will be sent update & delete events for each post';

create table parsav_artifacts (
	id          bigint primary key default (1+random()*(2^63-1))::bigint,
	birth       bigint not null,
	content     bytea, -- if null, this is a "ban record" preventing content matching the hash from being re-uploaded
	hash		bytea unique not null, -- sha256 hash of content
	-- it would be cool to use a computed column for this, but i don't want
	-- to lock people into PG12 or drag in the pgcrypto extension just for this
	mime        text -- null if unknown, will be reported as octet-stream
);
................................................................................
);
create index on parsav_artifact_claims (uid);
create index on parsav_artifact_claims (uid,folder);
comment on table parsav_artifact_claims is
'a list of users who have an ownership interest in each artifact (effectively an index of GC roots)';

create table parsav_circles (
	id          bigint primary key default (1+random()*(2^63-1))::bigint,
	owner       bigint not null references parsav_actors(id) on delete cascade,
	name        text not null,
	members     bigint[] not null default array[]::bigint[],

	unique (owner,name)
);
create index on parsav_circles (owner);

create table parsav_rooms (
	id          bigint primary key default (1+random()*(2^63-1))::bigint,
	origin		bigint references parsav_servers(id) on delete cascade,
	name		text not null,
	description text not null,
	policy      smallint not null
);
comment on table parsav_rooms is
'an index of user-created chatrooms';
................................................................................
	title  text, -- admin-granted title like reddit flair
	vouchedby bigint references parsav_actors(id) on delete set null
);
create index on parsav_room_members (member);
create index on parsav_room_members (room);

create table parsav_invites (
	id          bigint primary key default (1+random()*(2^63-1))::bigint,
	-- when a user is created from an invite, the invite is deleted and the invite
	-- ID becomes the user ID. privileges granted on the invite ID during the invite
	-- process are thus inherited by the user
	issuer bigint references parsav_actors(id) on delete set null,
	handle text, -- admin can lock invite to specific handle
	rank   smallint not null default 0,
	quota  integer not null  default 1000
);
comment on table parsav_invites is
'all active invitations and the level of authority they grant if accepted';

create table parsav_sanctions (
	id     bigint primary key default (1+random()*(2^63-1))::bigint,
	issuer bigint references parsav_actors(id) on delete set null,
	scope  bigint, -- can be null or room for local actions
	nature smallint not null, -- silence, suspend, disemvowel, censor, noreply, etc
	victim bigint not null, -- can be user, room, or post
	expire bigint, -- auto-expires if set
	review bigint,  -- brings up for review at given time if set
	reason text, -- visible to victim if set







|









|







 







|







 







|







 







|









|







 







|









|







 







|












|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
..
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
..
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
...
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
...
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
--	('policy-security',:'secmode'),
--	('policy-self-register',:'regpol'),
--	('master',:'admin'),

-- note that valid ids should always > 0, as 0 is reserved for null
-- on the client side, vastly simplifying code
create table parsav_servers (
	id     <def:uniq>,
	domain text not null unique,
	key    bytea,
	knownsince bigint,
	parsav boolean -- whether to use parsav protocol extensions
);
comment on table parsav_servers is
'all servers known to the parsav instance. the local server (including its private key) is stored in row (id = 0)';

create table parsav_actors (
	id        <def:uniq>,
	nym       text,
	handle    text not null, -- nym [@handle@origin] 
	origin    bigint references parsav_servers(id)
		on delete cascade, -- null origin = local actor
	knownsince bigint not null,
	bio       text,
	avatarid  bigint, -- artifact id, null if remote
................................................................................
	primary key (key,actor)
);
create index on parsav_rights (actor);
comment on table parsav_rights is
'a backward-compatible list of every non-default privilege or deprivilege granted to a local user';

create table parsav_posts (
	id         <def:uniq>,
	author     bigint references parsav_actors(id) on delete cascade,
	subject    text,
	acl        text not null default 'all', -- just store the script raw 🤷
	body       text,
	posted     bigint not null,
	discovered bigint not null,
	chgcount   integer not null default 0,
................................................................................

	primary key (relator, relatee, kind)
);
comment on table parsav_rels is
'all relationships, positive and negative, between local users and other users; kind is a version-specific integer mapping to a type-of-relationship enum in store.t';

create table parsav_acts (
	id      <def:uniq>,
	kind    text not null, -- like, rt, react, so on
	time    bigint not null,
	actor   bigint references parsav_actors(id) on delete cascade,
	subject bigint, -- may be post or act, depending on kind
	body	text -- emoji, if react; complaint, if report
);
create index on parsav_acts (subject);
................................................................................
create index on parsav_acts (actor);
create index on parsav_acts (time);
comment on table parsav_acts is
'every simple action taken on a tweet by an actor, including likes, rts, reacts, and reports';

create table parsav_log (
	-- accesses are tracked for security & sending delete acts
	id    <def:uniq>,
	time  bigint not null,
	actor bigint references parsav_actors(id)
		on delete cascade,
	post  bigint not null
);
comment on table parsav_log is
'a log of accesses from foreign servers, tracking which will be sent update & delete events for each post';

create table parsav_artifacts (
	id          <def:uniq>,
	birth       bigint not null,
	content     bytea, -- if null, this is a "ban record" preventing content matching the hash from being re-uploaded
	hash		bytea unique not null, -- sha256 hash of content
	-- it would be cool to use a computed column for this, but i don't want
	-- to lock people into PG12 or drag in the pgcrypto extension just for this
	mime        text -- null if unknown, will be reported as octet-stream
);
................................................................................
);
create index on parsav_artifact_claims (uid);
create index on parsav_artifact_claims (uid,folder);
comment on table parsav_artifact_claims is
'a list of users who have an ownership interest in each artifact (effectively an index of GC roots)';

create table parsav_circles (
	id          <def:uniq>,
	owner       bigint not null references parsav_actors(id) on delete cascade,
	name        text not null,
	members     bigint[] not null default array[]::bigint[],

	unique (owner,name)
);
create index on parsav_circles (owner);

create table parsav_rooms (
	id          <def:uniq>,
	origin		bigint references parsav_servers(id) on delete cascade,
	name		text not null,
	description text not null,
	policy      smallint not null
);
comment on table parsav_rooms is
'an index of user-created chatrooms';
................................................................................
	title  text, -- admin-granted title like reddit flair
	vouchedby bigint references parsav_actors(id) on delete set null
);
create index on parsav_room_members (member);
create index on parsav_room_members (room);

create table parsav_invites (
	id          <def:uniq>,
	-- when a user is created from an invite, the invite is deleted and the invite
	-- ID becomes the user ID. privileges granted on the invite ID during the invite
	-- process are thus inherited by the user
	issuer bigint references parsav_actors(id) on delete set null,
	handle text, -- admin can lock invite to specific handle
	rank   smallint not null default 0,
	quota  integer not null  default 1000
);
comment on table parsav_invites is
'all active invitations and the level of authority they grant if accepted';

create table parsav_sanctions (
	id     <def:uniq>,
	issuer bigint references parsav_actors(id) on delete set null,
	scope  bigint, -- can be null or room for local actions
	nature smallint not null, -- silence, suspend, disemvowel, censor, noreply, etc
	victim bigint not null, -- can be user, room, or post
	expire bigint, -- auto-expires if set
	review bigint,  -- brings up for review at given time if set
	reason text, -- visible to victim if set

Modified crypt.t from [034fdb64c7] to [a12c25b6dd].

81
82
83
84
85
86
87
88
89
90
91
92
93

94
95
96
97
98
99
100
	else
		return lib.pk.mbedtls_pk_write_key_pem(key, buf, const.maxpemsz) == 0
	end
end

local binblob = lib.mem.ptr(uint8)
terra m.der(pub: bool, key: &ctx, buf: &uint8): binblob
	var ofs: intptr
	if pub then
		ofs = lib.pk.mbedtls_pk_write_pubkey_der(key, buf, const.maxdersz)
	else
		ofs = lib.pk.mbedtls_pk_write_key_der(key, buf, const.maxdersz)
	end

	return binblob {
		ptr = buf + (const.maxdersz - ofs);
		ct = ofs;
	}
end

m.destroy = lib.dispatch {







|





>







81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
	else
		return lib.pk.mbedtls_pk_write_key_pem(key, buf, const.maxpemsz) == 0
	end
end

local binblob = lib.mem.ptr(uint8)
terra m.der(pub: bool, key: &ctx, buf: &uint8): binblob
	var ofs: ptrdiff
	if pub then
		ofs = lib.pk.mbedtls_pk_write_pubkey_der(key, buf, const.maxdersz)
	else
		ofs = lib.pk.mbedtls_pk_write_key_der(key, buf, const.maxdersz)
	end
	if ofs < 0 then return binblob.null() end
	return binblob {
		ptr = buf + (const.maxdersz - ofs);
		ct = ofs;
	}
end

m.destroy = lib.dispatch {

Modified mgtool.t from [4fc07a70c1] to [63607cdb2b].

284
285
286
287
288
289
290
291
292
293
294
295
296
297
298



299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
				return 1
			end
			if dbmode.arglist.ct < 1 then goto cmderr end

			srv:setup(cnf) 
			if lib.str.cmp(dbmode.arglist(0),'init') == 0 and dbmode.arglist.ct == 2 then
				lib.report('initializing new database structure for domain ', dbmode.arglist(1))
				dlg:tx_enter()
				if dlg:dbsetup() then
					srv:conprep(lib.store.prepmode.conf)

					do var newkp = lib.crypt.genkp()
					 -- generate server privkey
						var kbuf: uint8[lib.crypt.const.maxdersz]
						var derkey = lib.crypt.der(false,&newkp, kbuf)



						dlg:server_setup_self(dbmode.arglist(1), derkey)
					end

					dlg:conf_set('instance-name', dbmode.arglist(1))
					dlg:conf_set('domain', dbmode.arglist(1))
					do var sec: int8[65] gensec(&sec[0])
						dlg:conf_set('server-secret', &sec[0])
					end
					lib.report('database setup complete; use mkroot to create an administrative user')
				else lib.bail('initialization process interrupted') end
				dlg:tx_complete()
			elseif lib.str.cmp(dbmode.arglist(0),'obliterate') == 0 then
				var cfmstr: int8[64] gen_cfstr(&cfmstr[0],0)

				if dbmode.arglist.ct == 1 then
					lib.bail('you are attempting to completely obliterate all data! make sure you have selected your target correctly. if you really want to do this, pass the confirmation string ', &cfmstr[0])
				elseif dbmode.arglist.ct == 2 then
					if lib.str.cmp(dbmode.arglist(1), cfmstr) == 0 then







|






|
>
>
>










|







284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
				return 1
			end
			if dbmode.arglist.ct < 1 then goto cmderr end

			srv:setup(cnf) 
			if lib.str.cmp(dbmode.arglist(0),'init') == 0 and dbmode.arglist.ct == 2 then
				lib.report('initializing new database structure for domain ', dbmode.arglist(1))
				--dlg:tx_enter()
				if dlg:dbsetup() then
					srv:conprep(lib.store.prepmode.conf)

					do var newkp = lib.crypt.genkp()
					 -- generate server privkey
						var kbuf: uint8[lib.crypt.const.maxdersz]
						var derkey = lib.crypt.der(false,&newkp, &kbuf[0])
						if not derkey then
							lib.bail('could not write out DER form of server pubkey!')
						end
						dlg:server_setup_self(dbmode.arglist(1), derkey)
					end

					dlg:conf_set('instance-name', dbmode.arglist(1))
					dlg:conf_set('domain', dbmode.arglist(1))
					do var sec: int8[65] gensec(&sec[0])
						dlg:conf_set('server-secret', &sec[0])
					end
					lib.report('database setup complete; use mkroot to create an administrative user')
				else lib.bail('initialization process interrupted') end
				--dlg:tx_complete()
			elseif lib.str.cmp(dbmode.arglist(0),'obliterate') == 0 then
				var cfmstr: int8[64] gen_cfstr(&cfmstr[0],0)

				if dbmode.arglist.ct == 1 then
					lib.bail('you are attempting to completely obliterate all data! make sure you have selected your target correctly. if you really want to do this, pass the confirmation string ', &cfmstr[0])
				elseif dbmode.arglist.ct == 2 then
					if lib.str.cmp(dbmode.arglist(1), cfmstr) == 0 then

Modified route.t from [db07af2616] to [56f7ddd740].

510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
					end
				end
				return quote
					var [me]
					[check]
				in [me] end
			end)()]
			privs:dump()
			if privs:sz() > 0 then
				lib.dbg('installing credential restrictions')
				lib.io.fmt('on priv %llu\n',aid)
				co.srv:auth_privs_set(aid, privs)
			end

			lib.dbg('setting netmask restrictions')







<







510
511
512
513
514
515
516

517
518
519
520
521
522
523
					end
				end
				return quote
					var [me]
					[check]
				in [me] end
			end)()]

			if privs:sz() > 0 then
				lib.dbg('installing credential restrictions')
				lib.io.fmt('on priv %llu\n',aid)
				co.srv:auth_privs_set(aid, privs)
			end

			lib.dbg('setting netmask restrictions')