In this file: Features and fixes to maybe implement in the future.
Each issue starts with a "code tag", e.g.:  [subcats]
which you can search for across the whole code base, to find some-of / all-of
the relevant source code places to change.

[emburl_emgurl] [prefix_diid]
"emburl" is likely interpreted as "embedded url", but in the API it means the
embedd*ing* url, and I used to "always" get confused by what I had meant when
I saw "emburl". What about "emg" for "embedding"? So:
Change from emburl:.. to emgurl:..  no, to  emgurllax:.. and emgurlexact:...  ?
Hmm or  emgurl:  and emgurlexact:  maybe is better — "lax" can be the default?
And prefix w 'diid:' in alt_page_ids3. Rename to page_(lookup)_keys_t?


Instead of this file, maybe use a combination of:
 - https://github.com/dspinellis/git-issue   wow! and source code with  {{ {1  vi folds
 (- https://git-scm.com/docs/git-notes? )
 (- https://github.com/google/git-appraise )

[dynamic_max_limits][rate_limits]
SHOULD NEXT: Make these limits configurable at runtime, and trigger superadmin notifications
when exceeded 50%, so can review and bump. And warnings if exceeds 75%, 90%.

[first_last_apr_at]
Fix sort order bug: Add Post.firstApprovedAt.

[stale_stats]
Popularity calcs sometimes ignore the very latest comment / chat msg?

[timeline_comts] Maybe. Rename "progress posts" to "timeline comments"?

[purp_dep_text]
Help texts that are different, depending on the purpose of the forum. E.g. calls people
"employees" and "customers",  or if it's for education: "teachers", "students".

[ty_themes]
Primary button colors, secondary, background, arrows, fonts,
and embedded widgets, e.g. Twitter's oEmbed has a 'link_color: #223344' attr,
                            and  theme=light/dark.

[post_owners] posts_t.owners_id_c — if ownership of a post has been transferred.
The new owner can, but the original author can't, edit it and delete it etc.

[post_authors] if many authors, or different from the creator.

[post_rels_t] New table, will store relationships between posts and whatever, e.g.
post A is-answer-to post Q (could be many answers), multireplies, here.

[drafts_as_posts] Move drafts from drafts3 to posts_t?

[flags_as_posts] Move flags from  post_actions3 to  posts_t, and use a future
table  post_rels_t  to link to flagged posts or user accounts.
Why? See <./tyworld.adoc>, the Flag Pages and Posts section.

[do_it_votes]
Optionally, use separate Do-It and Do-Not votes, to show if one wants
to implement an idea or not — in addition to Like votes. Sometimes,
a feature request / idea might be well researched & make sense,
and it's good to Like it, although for reasons the poster couldn't
have known about, Do-Not vote.

[joint_decisions]
See:  wip/joint-decisions/joint-decisions.txt


[pat_rels_t]  Rename & change post_actions3  to  pat_rels_t, and store
votes and AssignedTo there.

[perms_on_pats_t] Rename group_participants3 to perms_on_pats_t, for consistency.

[cluster_tables] Cluster posts_t on page id, and *_rels_t on the from_.. or to_.. id?
So all posts and links needed to render a page can get loaded quickly.
https://www.postgresql.org/docs/current/sql-cluster.html 


[wiki_links] — link to Ty page via its name, like at Wikipedia etc.
Stored in links_t, just like "text text  <a href=...>" text links.

[wiki_perms] [new_perms]
Make page props, e.g. answer-post, doing-status, page type, etc wiki-editable?
Rather than only individually, per post / comment.
Wikifiable things: Title, properties (doing-status, answer-post etc), Orig Post, replies.
What should be wiki-editable? Just the Orig Post? Or also replies, or page-props?
Probably per post (e.g. per OP or comment) is a good default / what people expect?
Maybe split "Wikify" into "Wikify text" and "Wikify properties" and "Wikify all"? Hmm

There could be a separate: May-wikify-own-posts permission,  and May-wikify-others-posts?
Indicating that somwhat granular permission control of a posts props can make sense?

[page_props_perms]
If pat 1) may edit the Orig Post text, and if pat 2) may change the page type, doing-status
etc, maybe should be different permissions? Or not?

[staff_can_see]
Add a black hanglock next to things one can see, because of one's extra
access permissions? E.g. editing category settings, or backlinks from
access restricted topics.

[admin_log]
Make site specific errors visible to site admins — so no need
to have Linux root access.

[logger_everywhere]
Pass loggers to the models and rdb Scala modules, so they don't need to use
System.stdout/stderr directly.

[immutable]
Make all js objs "light weight immutable", via Typescript's 'readonly' properties:
https://www.typescriptlang.org/docs/handbook/interfaces.html#readonly-properties
(React wants immutable.)

[add_nodes_t]
Merge  posts3, pages3, categories3 and some settings3 columns into:  nodes_t
(which will actually be posts3 but renamed, and with some new columns).
But most such settings should instead be in:  pat_node_multi_rels_t  (!).
See: ./everything-is-a-node.txt

[subcats] [sub_cats]
DONE: Implement sub categories.
But missing: Search in sub cats, if searching in base cat.

[forum_page]
More configurable, & discussions too, see:  y2999/wip_sect_disc_props_views_stats.sql

[watchbar_to_linkbar][metabar_2_pagebar]
RENAME the "watchbar" to "linkbar", the page meta bar to "pagebar", and the right
sidebar to "contextbar" ?

[propagate_cat_perms]
It'd be nice if there was a way to get an overview of sub cat permissions,
when looking at a base cat. And if one could propagate the base category's
permissions to all/some sub cats.
Also make this work for the root cat? So one can get an overview of permission
settings for *all* base cats and sub cats in the whole forum — a cat permissions
table-tree, somehow. [incl_root_cat]

[may_not_perms] (negative_perms, neg_perms, [deny_perms])
Currently permissions are additive. Maybe, though, in some cases it'd be good
with "negative" permissions — e.g. a group that may Not something.
Then, everyone in that group, would be forbidden from doing that something,
regardless of how they inherited permissions from other groups.

[0_site_perms]
Let's wait. Whole site see/reply/edit/etc permissions haven't been totally implemented —
there's no way to set such permissions via the ui (right?).
Maybe that's good — site wide permissions would be too coarse grained & permissive,
would almost need to be combined with Deny permissions? [may_not_perms] So that it'd
still be possible with private categories, even if sbd may see everything by default.
But there's group permissions — that's site wide perms, but not related to any
specific category, so, fine (no confusion about if inherited in the category tree, since
not related to categories). See EffPatPerms.

[granular_perms]
E.g. may-see-others'-sessions. Currently only amdins may, but sometimes, good if mods could too?
Or a mayReplyOnOwn permission? Maybe never.
Also see: [wiki_perms]

[inherit_group_priv_prefs]
Privacy preferences are inherited from groups, and there's a group priority order see:
../wip/priv-prefs/priv-prefs-wip.txt and Scala class MemberPrivacyPrefs.
Visualized on the inspect page, ../client/app-staff/admin/inspect.staff.ts.
But not implemented everywhere: [inherit_priv_prefs_0impl]

[maySeeProfiles](WithMaxTrLv?)
New permissions, on groups. Can then see profiles, or maySeeActivity, or mayMessage etc.
Might want to grant to all support staff, in a publ forum, f.ex., even if they're not moderators.

[group_priorities]
Let admins configure which groups have priority, if there're conflicting privacy
preferences? Trust level groups have increasing priority from Everyone —> All Members —> ...,
Custom groups priorities: See  ../wip/priv-prefs/priv-prefs-wip.txt.

[tags]
Nice props / tags in Gerrit: https://chromium-review.googlesource.com/c/v8/v8/+/2537690

[missing_tags_feats]
Wasn't fixed in first iteration.
+ orig post author user flair (badge).
+ might as well add search-by-user-id/username/full-name at the same time?
  And upgr ES to 8.x.
+ search for all JsUser(_) — then tags (badges) not included.
+ get notified about posts w tags one follows
+ for some endpoints, [tags_and_badges_missing]
+ access restricted tags [priv_tags]

+ [tag_perms] Grant create/edit tag permissions to groups or trust levels?


[nested_tags][child_tags] hierarchical tags

[to_paginate]
E.g. don't list only 100 tags — add a Show More ... button.

[sw] [sse]
DONE: Service worker.  No, skip, using Websocket instead of:  Server sent events.

[spam]
When deleting a post, ask if it's because it's spam. If yes, and the post
hasn't been spam checked — send it to the spam check services so they'll
get more training data. [DELSPAM] (Currently, only if the post has already
been spam checked, will it get sent back to the spam check service,
if there was a classification error.)

If many posts by a new user are classified as spam. Or if staff deletes most of hens
posts, during review. Then mark user as a moderate threat. [DETCTTHRT]

FIXED, now uses 1st party cookie or storage: [privacy-badger]:
Privacy Badger doesn't like transient session cookies, currently.
(See https://github.com/EFForg/privacybadger/issues/2003#issuecomment-391745819  bullet point 3. )
So maybe change from csrf Double Submit Cookie, to Encrypted Token Pattern?
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Encrypted_Token_Patternp

[gdpr]
New table: sessions_t, impl fn deletePersonalDataFromOldSessions().  [sess_deleted_ix]

[pps]
Rename User to Participant and User to Member.  Participant = Guest or Member, Member = User or Group.
Rename variables named like "member" to "user", "memberOrGroup" to "member", "user" to "participant"
Rename "indie" to user" and "people" to "members"

[private_chats] Later.

[private_pats][some_pub_priv_prefs]  Unlisted / less-visible / "hidden"? users?
So cannot see / list users unless has access to the same categories, or is in
the same group(s) or high enough trust level or sth like that?
The opposite: Easy to find pats, e.g.:
      type ' @' and the '@support' group is suggested [mentions_prio_c].

[dm_priv_pats]
If happens to be in same discussion as a private user, can be nice to be able to DM
them, although can't access their profile and see any more user details.
(maySendMeDmsTrLv can be configured independently.)

[tech_level] Let users configure their tech level — let's say sbd who knows all about
Ty joins a new forum. Then, although a New Member, hen can up hens tech level and get
access to features the software otherwise would have thought were too complicated.
Store in pats_t.tech_level_c.

[new_trust_levels]
[mods_are_core_membs]
Let trust level of mods & admins be CoreMember — or maybe add higher trust levels?
Can start at 21 = [lightweight_mods], 22 = mod, 23 = mod of mods, 24 = admin, 25 = owner?

[alterPage]
[core_move_page]
Should there be a move-page-perm, not just alter-page?

[power_mod]
[power_users]

[power_admin]
Various fancy admin features, disabled by default.

[mod_of_mods]
? Like admins, but cannot edit site configuration (and might not have access to all parts).
Can manage [lightweight_mods] and ordinary mods.

[lightweight_mods]
? People who can approve posts / edit / suspend, but fewer abilities than ordinary mods.

[cat_mods]
Category moderators. They'd be [lightweight_mods], but in a specific category/ies only.


[user_version] [tag_versions]
Add a version field to pats_t and an in-mem user? So knows if the in-mem one is stale;
then, the browser shouldn't use it, if it already has a newer version.
Also add tagtype_t.version_c  and tags_t.version_c, so we'll know, client side,
for these too, which one is the most up-to-date, or equally up-to-date, if we have two.


[security]
prevent usernames from being displayed in Unicode, if mixed charsets used,
e.g. Latin + Cryllic = facebooĸ.com  (homograph attacks).

Switch to firewalld.

use gVisor / runsc or Firecracker.
See: https://fly.io/blog/sandboxing-and-workload-isolation/

Optionally encrypt off-site backups? Maybe:  https://github.com/borgbackup/borg
    (plus instructions to copy the backup encryption key!)
    Seems ppl like it:  https://news.ycombinator.com/item?id=21642364  "the holy grail"
      rsync.net

Add CSP to all page types / routes: CSP_MISSING, [script_src_self].

ZAProxy auto scan.
https://docs.gitlab.com/ee/user/application_security/dast/index.html

[repr_builds]
Reproducible builds.

[rate_limits][to_rate_lim]
Configurable rate limits.

[precompile_luajit]  COULD_OPTIMIZE  SKIP, instead [use_pingora]
See: https://blog.openresty.com/en/luajit-bytecode/
Many times faster startup time, hopefully uses less memory too?

[server_limits] [site_limits]
If admins try to configure too "high" values, e.g. 1 TB upload size,
need a way for them to know they're above the server or site limits,
e.g. the Nginx client_max_body_size.

[site_ip_person_limits]
Should be 3 types of rate & max limits multipliers:
1) One that affects groups and everyone in those groups,
2) One that affects ip addresses, possibly specific addresses or whithin a country,
3) One that affects the whole site.
But the current  SiteLimitsMultipliers  are for all three at once — which doen't make sense.
E.g. shouldn't increase *per person* rate limits, just because there's many members.
Instead, should split into 1, 2, 3 above.
And, with 1) limits for user groups, then, the Everyone group would be the default
limit multipliers (1.0), and e.g. Core Members could have limits x 2?
Sth like:
    SiteLimitsMultipliers —> LimitsMultipliers,
      and look up by:  whole site,  ip addr logged in / not logged in,  user group?
      and look at all limits.
Add more fields to  PatPerms? No, it's not for limits, but for permissions?  [more_pat_perms]

[more_runtime_assertions]
Figure out a way to safely add more runtime assertions — although sometimes
I'll add *incorrect assertions* some of which will *break* and need to
be disabled.

[authn]
What about adding U2F login?
https://github.com/Yubico/java-webauthn-server/ — a Java U2F / WebAuthn lib.

Adding more authn methods:
When logged in, click an Add-another-login-method button. Then choose between
e.g. Gmail, GitHub etc, and after logging in there, add that identity
to the user account.
That's how e.g. StackOverflow does this:
  http://stackoverflow.com/questions/6487418/
       how-to-handle-multiple-openids-for-the-same-user

[oidc_missing]:  some more nice OIDC things.



[srv_glb_idp]
Link to server global idp:
  * @param idpSiteId — None, unless is from a server global
  *   authn site, with e.g. shared Gmail or Facebook OIDC or OAuth2 config,
  *   and that authn site id is different from the current site id.
Aha!? I'll just add is_server_global_c and guid_c fields to idp_t,
and change idtys_t.conf_file_idp_id_c to lookup IDPs both via conf file,
and idps_t.guid_c. And rename conf_file_idp_id_c to glob_idp_guid.
Migrate to ScribeJava, [migr_to_scribejava]


[per_site_email_domain]
Optionally configure email settings in the admin area, per site.

[no-email]:
Let people create accounts without specifying any email address. Like at Reddit.

[many_emails] [email_privacy]
Let someone create many accounts with the same email address.
Also then one cannot try to find out if an account exists,
by specifying that person's email addr.
And when logging in via email, one gets to choose which account to login to.

[known_verified_email_domains]
      // + known all-email-addrs-have-been-verified email domains from site settings?

[clean_up_emails]
Refactor a bit.
Search for:  [4KDPREU2], break out fn.

[old_users_verif_email]
Add a setting that says if already active accounts w unverified email addrs,
need to verify their addrs to continue using them — or if the requireVerifiedEmail
setting applies only to *new* signups.  If old users need to verify too, then,
add a button to send all of them an email addr verification email?
(Maybe not actually send all emails exactly at once though.)

[save_email]
Save invitation emails sent (and other types I forgot?), & any SMTP erors.

[reenable-akismet]:
For some reason I disabled Akismet (why? when refactoring to save time?). Enable it again.
Conf vals for enable/disable: check ip addr, country, sign-up email addr, aksimet,
toxicity/content-filter, image scan, other uploads / check hashsums, outgoing links.

[hard_deletes]

[undel_pages]
DONE: Should remember who deleted it, so can see one's own deleted pages and undelete them,
but cannot seem one's own pages if instead they got deleted by staff.
In fact, all pages_t.deleted_at, frozen_at,  etc,
   should instead be   deleted_by, frozen_by, etc
   — because always good to remember who did it; often, that person may undo (+ staff).
   But *when*, that's for the audit log.
Missing: Improve the UX.

[undel_posts] [62AKDN46] [apr_deld_post] [apr_movd_orig_post]
Undelete posts, not just pages and categories.
And make it possible to change from  Rejected —> Approved,
and Approved —> Rejected,  unless got replies or Like votes already?
Need an approvalStatus [ApprovedStatus] field?
 -3 = rejected by staff
 -2 = rejected by staff, approved by system  (+1 -3 = -2)
 -1 = auto blocked by system (e.g. spam)
  0 = undecided
  1 = auto approed by system
  2 = approved/reviewed by staff, but initially auto-blocked by system (-1 +3 = 2)
  3 = approved/reviewed by staff
Or:  0,1,2 = undec, apr, rej by sys.  4,8,16 by trusted,  core member,  32,64,128 by staff  ?
   bits 1,2                           3,4                 5,6           7,8

[7YKU24] UNPOLITE SHOULD stop auto approving chat messages.

[flat-comments]:
Change layout of problem type topics to traditional forum flat comments layout.
& use for formal messages too, or maybe repl w chat?

[threaded_chat]
A chat with indentation depth  > 1, would be threaded? Not yet supported, but why would be
interesting to try.

[editor-drafts]:
When starting a new topic, or editing a post, ask "Continue editing draft? [Yes, resume draft] [No, ignore draft (but keep it)]"
instead of always opening the old draft. Because otherwise sometimes old edits I didn't want, reappears.
How do that, UX wise? Maybe ask, when *opening* instead of when *saving*? and always
save a draft by default? Because maybe short of time when saving (e.g. need to run and
catch the subway, happens to me all the time) then it's annoying with questions.

[relfollow]:
Add a 'rel=follow links to domains: ...' config value,
so peope can follow links to their own domains.
Domains where rel=nofollow should not be added to links.
Will be applied to sub domains too, e.g. if you type `ex.com`, `sub.ex.com` will also
have rel=nofollow removed.
You should add the address of this forum, and your main website (if any),
so search engines like Google, Bing, Yandex, Baidu will find all content.


[rand-page-id], [rand-user-id]:
Let outwardly visible page and user ids, be random numbers, to prevent
discovery of pages & people by iterating through all ids in the url.
64 bit random ids? or 32 bits + stricter rate limiting?

[custom-groups]
Search for that tag.

[pseudonyms_later]
Anonyms and pseudonyms, see:  wip/aliases/wip.txt

[assign_anon]

[assign_comments]

[subcomms] [sub_communities]
Improve sub communities, e.g. selecting a category across all sub communities,
not only the current one.

[large_pages]
Support pages with 10 000+ replies or 10M replies.
(Some real life blog posts get 2M comments!)

[performance]
Use:  ch.qos.Logback.classic.AsyncAppender, measure req/s bef & aftr.
Nginx: error_log  debug vs warn.

Performance tests:
 "Capture a timeline trace of your site to help diagnose performance issues."
 https://github.com/GoogleChrome/puppeteer

Test on real life anonymized data:  PostgreSQL Anonymizer?
   https://www.postgresql.org/about/news/2017

[overcommit_memory]
Postgres might get OOM-killed if overcommit enabled. Maybe edit installation
instructions to disable overcommit?
vm.overcommit_memory = 2   (the default is 0 = yes)
vm.overcommit_ratio = 75   (how much, in %, of the mem + swap the kernel may overcommit)
https://news.ycombinator.com/item?id=27794237 CloudSQL Postgres misconfigured OS OOM killer
https://stackoverflow.com/questions/52148675/aws-rds-with-postgres-is-oom-killer-configured

[when_pg_smarter]
Can remove fk indexes?:
  tags_i_parentid_patid
  tags_i_parentid_postid
And these unique indexes:
  tags_u_id_patid
  tags_u_id_postid

[careless_rdb]  ty_crdb
Use a 2nd PostgreSQL server for a persistent mem + disk "cache"?
E.g. for session storage and user visit logs.
Set isolation level read-committed, https://www.postgresql.org/docs/current/transaction-iso.html
and synchronous_commit = off,
    https://www.postgresql.org/docs/current/wal-async-commit.html
    https://www.postgresql.org/docs/current/runtime-config-wal.html#GUC-SYNCHRONOUS-COMMIT
and increase commit_delay
and set commit_siblings?
    https://www.postgresql.org/docs/current/runtime-config-wal.html#GUC-COMMIT-DELAY
    See: https://stackoverflow.com/a/58169528
see https://dba.stackexchange.com/questions/42290/configuring-postgresql-for-read-performance
https://www.cybertec-postgresql.com/en/postgresql-vs-redis-vs-memcached-performance/
>  Asynchronous mode was used for writing data, meaning a slight loss of most recent data is possible in case of a server crash
Could move to the careless rdb?  "AC" means async-commits-probably-ok
    (Change the suffixes to "_ct", meaning "...cache table" or "... careless table".
    user_visit_stats3 AC
    page_html3  — is just a cache. AC
    user_stats3?
    spam_check_queue3
    sessions_t   — AC if a session is lost, all that happens is that one needs to log in again.
              When logging out, then, that transaction should be synchronous (not async) though.
    review_tasks3 — No! That's important, don't move to the careless rdb.
    post_read_stats3 AC ?
    page_popularity_scores3 ??
    link_previews_t AC — who cares if a Twitter tweet wasn't saved, has to be fetched again.
    index_queue3
    emails_out3
    audit_log3 — split in 2 parts maybe?  One less interesting, and one important?

[table_partitions]
Shard tables by site_id_c? {{{2
Needs pg11 (not pg10): In pg11, a parent table index gets auto-created on child tables
(both child tables that already existed, or get created after the index).
What happens when partitions created and there's data already?
Ans:
  > It is not possible to turn a regular table into a partitioned table or vice versa
  https://www.postgresql.org/docs/current/ddl-partitioning.html
Ok, so partitioning means renaming the tables, and creating new, w partitions,
then copying the old data?
(Not needed in Ty's case, but pg11 supports updating rows and auto-moving
to the correct partition.)
Actually, needs pg12: foreign keys fully compatible with partitioned tables:
    can have partitioned table on either side of a foreign key constraint.
    (Ty's case: always both sides.)
    > for the first time it is possible in PostgreSQL to maintain large volumes
    > of data while maintaining referential integrity
    https://www.2ndquadrant.com/en/blog/postgresql-12-foreign-keys-and-partitioned-tables/
    pg11: enable_partitionwise_aggregate
Nice way to quickly delete a whole site: Just drop the whole partitioned tables.
  > very quickly delete millions of records because it doesn't have to individually delete every record

Pg12 has good partition pruning: at ~ 8k partitions, speed is ~90% of w just 1 table, and ~100% up to
about 2k partitions. https://www.2ndquadrant.com/en/blog/postgresql-12-partitioning/
}}}2

[cluster_tables]
Or, cluster tables — but:
  > when the table is subsequently updated, the changes are not clustered
  https://www.postgresql.org/docs/current/sql-cluster.html
  One can periodically recluster — but that requries an exclusive lock!
  However, clustering posts3 / posts_t by page_id_c = good idea.
  There is also:
  https://github.com/reorg/pg_repack, clustering without exclusive locks, a fork of:
    https://github.com/reorg/pg_reorg  (abandoned)


DONE [onesocket]
Subscribe to live-updates via a "shared web worker" so each browser gets its own connection,
rather than each browser tab.  See e.g.:
https://stackoverflow.com/questions/9554896/sharing-websocket-across-browser-tabs
websearch for e.g. "chrome all tabs share websocket"


[canonical-username]
[CANONUN] [5WKB23Z]
Rename theUsername to exactUsername? — it'll be how the username is displayed,
e.g. `jane_doe` or `jane.doe`, and always possible to login with that exact username.
Add canonicalUsername, which is: lowercase, and /[._-]+/ changed to '_'
so there's nothing but: [a-z0-9_]+ left.
By default, one can type whatever [_.-] when logging in, and it'll be first checked against
the exact username, and then [_.-]+ changed to '_' and compared with the canonical username.
And won't be able to create new accounts that have the same canonical useranme, as an already existing.
So logging in as Jane Doe works with: 'jane.doe', 'jane-doe', 'jane_doe'. And also for mentions,
so all these work: `@jane.doe @jane-doe @jane_doe` — it'll be the same person.
However, `jan.edoe` is someone else. That canonical username is different ('_' in a different place).

[username-starts-with-_] ?
https://github.com/discourse/discourse/commit/262f561a877e0296d7ca7f6ec9f27edd0d30ca8e
https://github.com/discourse/discourse/commit/90351348ec3c0b1872aa680c0c9ddd5271e3740b
https://meta.discourse.org/t/usernames-with-periods-are-changed-to-underscore/17168/8  . –> _


[canonical-email]
Avoid accidentally splitting an account into two, because sometimes '.' sometimes no '.'
when logging in with Gmail. Also, @gmail.com and @googlemail.com are the same.
https://support.google.com/mail/answer/10313?hl=en
Real life example of this happening:
https://meta.discourse.org/t/discourse-creates-new-users-if-dots-are-present-absent-in-google-email-address-when-logging-in-using-google/66151
Good approach?: https://stackoverflow.com/a/51344711/694469

Add canonical email? [canonical-email] So  dotty.addr.ess@googlemail also
saved as  dottyaddress@gmail.com, and if logs in with or without dots,
we'll know it's the same person? However, if types  some.name+tags@gmail.com,
then one *intentionally* added +something, and then treat as separate addr?
so can create different accounts for testing. (Spam detection could just
be aware that x+something and x+something2 and x+... are all the same accounts.)
If not, then sometimes people sign up manually, say, without dots. And then,
later, the community adds Gmail OpenAuth, and the same person signs in
with Google, and Google sends the addr via OpenAuth *with* dots, and this would
result in two different accounts for the same person, and confusion ("Where are
all my old posts?").

[gmail_dot_dash_friendly]
Gmail ignores '-' and '.' in email addersses — Talkyard could do that too
for @gmail.com addresses. However maybe leave "+..." as is, since people add
that "magic suffix" intentionally.

[email_casing]
Talkyard already converts email to lowercase  before storing in the database.
However theoretically this is a small security risk,
in that if there is any email provider somewhere that treats
emails as case sensitive, then, people using that provider,
could try to hijack Ty accounts owned by other people using that same
dangerous email provider.  But any such email provider is unsafe
already because people who write emails, don't type casing properly
anyway (how is anyone supposed to remember to type "Jane.doe@..."
instead of "Jane.Doe@...") and would send emails to the wrong recipient
"all the time", so using such an email provider is a dangerous idea in
the first place?
SECURITY; COULD  send verification emails to any email addresses with
different casing, so all addresses involved get to confirm
that they're the same real life person.
https://stackoverflow.com/questions/9807909/are-email-addresses-case-sensitive
Discourse has case *in*sensitive emails:
https://meta.discourse.org/t/create-account-email-address-check-should-be-case-insensitive/15576/9
> This problem is fixed. Email is not case sensitive anymore!
Maybe skip this forever and move to decisions.adoc instead?


[one-db-writer]
Prevent all PostgreSQL serialization errors, by doing all writes from a single actor, and
use message passing & async replies?


[change-author]
Make it possible to change the author of a post to someone else. Or change edited-by to another user id.

[assign-posts]
Assign topics and posts to people. (Not only whole topics, but also comments/sub-threads inside topics.)
Why assign a post? Because someone might ask a good question, in a longer discussion, and nice
to know & remember who will try to answer it, later.

[assignees_badges]
If listing assignees, should any badges of theirs be shown too? (Currently aren't.)

[can_see_assigned_c]
New permission.

[comment_tasks]
Later, comments can be tasks with their own DoingStatus, and marked done or closed.
(The db is prepared for this, but not implemented UX wise.)

[priv_comts] Private comments:
(Aka "whispers" in Discourse and "private items" in Basecamp long ago, and "private comments"
in other software.)
Add posts_t.private_pats_id_c, and a new participant type:  lists of pats,
to remember who can see private comments.
New permissions
        - Who may post/reply-to/delete private comments.
            Or no?  Instead, this'll be *the same* as who may post direct messages (DMs)
                  because private comments are *the same*, it's just that they
                  appear inline in another discussion, instead of on a separate page.
            So,  a single Who-may-post-private-messages setting that applies
            to both DMs and private comment sub threads.  [can_post_private_c]
        - Who may add, remove, manage users from/in private comment threads? Hmm.
          That'll be a  pats_t.how_private_c setting?
Ex: Privately talk with mods, directly next to the comment being discussed.
    Or a support group asks the Original Poster,
      in a support topic for tech details; then the maybe new-member can reply,
      and only other support staff can see hens tech details.
      (The UX would need to make it very clear that no one but staff can see the reply.)

Yes?: Private comments nrs can be big & random & < 0?
    Otherwise, people can look at post nrs and look after gaps in the number
    sequence (if > 0) and know that maybe there're private comment sub threads.

Don't incl priv comts when caching a page — only load lazily if current user can see.

Skip: Should there be a priv_comt_root_id so, if loading a descentant privt comt,
    it'd be immediately clear what post id to specify, to look up who can see that
    descendant priv comt? — Or load & look at all ancestors? Probably better,
    if a private thread is moved to another private thread but with different who-may-see
    — then, need to compute the intersection of who-can-see, to find out who can see
    the thread at its new location. Just looking at a single ancestor, wouldn't be enough.

Later, use private comments also for direct messages?
    Then can remove: page_users3.joined_by_id,  kicked_by_id  and lots of app server code?

Don't bump page if private comments — or do, for those who can see? Hmm.
      Do send notifications about priv comts to those who can see them & want though.
      Same notf level as for direct messages — instead of the page's notf level?


[see_who_notfd]
When composing a message, see who will get notified — e.g. if mentioning a group,
or editing a message, can be unclar if a newly edit-added @username gets notified or not
(they do get notified).

[filter_mentions] Render usernames that couldn't be mentioned in gray?
[filter_links] Don't link to bad places, e.g. malware site — show such links in plain text
               maybe with a warning? (depends on the community)


[mentions_prio][mentions_prio_c]
Maybe let  0@username generate a link, but no (zero) notification? or ...
       ... [@username]? no that's harder to type?
           =@username could be a looower prio notification, the software won't send any
                      email notf, only show if one visits the forum?
           -@username be a lower prio notification, deliver in a batch per daily
            @username be a low prio notification, deliver every <custom> hours
  no ——>   +@username or ...
  |    ... !@username could be an urgent notification, like, "I'm blocked until answered"
  |       !!@username be a really urgent notification, like, "Server is down"
  |        @@username alternatively?  But '@@' almost looks like just: '@'.
  |               '!@' is more clear?
  |     or 9@username could be high prio? hmm but '!' is maybe more intuitive.
  `\
    `—— Instead, maybe:
        +@username  could subscribe that user to notifications about new comments on that page?
            Some communities relies on mention-to-subscribe quite a lot.
            If it's a private chat, @username gets added to the chat members and can
            thereafter see the chat (& gets notified about new messages).
            But @username only links to hens profile, won't add to the chat.
            And maybe:
        ++@username  could *assign* the page to that user. (Would assign
           the user to the closest ancestor post that is a *task* — in the future,
           also comments can be tasks (mini tasks)  [comment_tasks].)

           Would  --@username  un-assign, then?
           What would  -@username do? Doesn't make sense to unsubscribe sbd else from notfs?


    (skip: ?@username be a question  — maybe it'd be better with marking comments as to-do?
          !?@username be an urgent question )

           @@username (two '@') could be a group notification?
                      E.g. Hi @@support, what [...]?  and probably @@support have
                      configued <custom> (see above) to be quick?
                      Ty would auto add the 2nd '@' even if one typed just 1, if it's a group?


[settings_templates]
Select one of a few pre-defined use cases (ways of using Talkyard), and have
a bunch of settings automatically tweaked, for that use case.
  - Organization internal
  - Public support
  - Teachers & students
  - Community
  - Blog comments


[tiny-dialog]
Find (or create?) a tiny modal dialog React class that can be included in slim-bundle.js,
so can open error dialog also if couldn't load more-bundle.js from the server.
Or cache more-bundle.js: PWA and service worker.


[manydrafts]
If there're many drafts for the same thing, show them in a list and let the user choose which
one to continue editing. (Rather than just picking a random one.)

[safer_imp]
If impersonating sbd, hide that person's aliases and subscriptions, by default.
So can e.g. help out w troubleshooting access perms, without seeing too much.
Maybe hide the person's posts?  Diferent impersonation capabilities?

[imp-groups]
Make it possible to impersonate groups. Nice for testing one's access control settings.
Currently disallowed, though, because haven't had time to verify doing things as a group,
wouldn't result in unexpected weird things. And who should be notified about replies to
a test comment made when impersonating a group?
(Everyone in the group? No, they'd get annoyed. Only the group then?)

[group-feats]
Add group visibility settings, e.g. no one (incl members in the group) can see
members in the group — except for some specific groups, e.g. staff.
And who-may-join feautre, and Join/Leave group buttons.
Don't let one self-join groups that grants a trust level higher than one's current level.

[sub_groups]

[cont_settings_t]
Per group and content set settings.
Might need more fine grained settings — per pages-in-category/tag/written-by-authors,
*for* various groups of people. Then,
pats_t, pat_groups_t,  cont(ent)_sets_t and cont_sets_meta_t
and cont_settings_t  can all be useful?  See [cont_settings_t] in db-wip.sql.


[message-managers]
A way to message only the managers in a group, rather than everyone in the group?
Maybe `@managers@group_name` ?

[req-memship]
Add a Request [group] Membership button (for groups that are visible, and that one may
not self-join). Group Managers and Adders then get notified
(Discourse: the 5 most recently active "group owners") — or, if there are none,
then the site staff get notified. Also, if the group managers don't handle the request
within X days, then the site staff get notified? (Passive group managers = not good.)

[lazy-upd-link-origins]
If moves CDN to a different address, or moves Talkyard server to a new address,
then, lazily regenerate html with the correct addresses, for links to uploaded
images etc.

[list_uploaded_files]
Admin page that lists uploaded files, sorts by name or size etc.
Optionally deletes file (initially, maybe just a Linux `rm ...` tips instead).

[sso_plus_oidc]
Let people combine both custom OIDC IDPs with Ty SSO — that's just
fine, some time large organization have more than one authn system,
or they invite external collaborators with other authn systems.

[paseto_v4_local]
Start supporting v4.local? But what js and java libs to use? The old js lib stopped working,
so currently using a small Rust PASETO cmd tool. See:
    ../tests/e2e-wdio7/specs/embcom.sso.token-in-cookie.2br.test.ts--e2e-crypto-probl.txt

DONE [blog_comments_sso]  emb_sso
Use PASTEO https://news.ycombinator.com/item?id=17877332
 http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-fo...
 http://cryto.net/%7Ejoepie91/blog/2016/06/19/stop-using-jwt-...
 https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-ba...
 https://paseto.io
Wow this is nice!:
     local: shared-key authenticated encryption
     Local tokens are encrypted
     https://github.com/atholbro/paseto#local-and-public
(Determinsistic json stringify: https://www.npmjs.com/package/fast-stable-stringify )

Expired PASETO token in htmlp page — then try fetch from authn server,
once iframe is in view — by asking any ssoRefreshAuthnTokenUrl endpoint.
Maybe rename "AuthnToken"  to "EmbAuthnTkn" ?


[offline-first]
Things to think about, if making Ty work offline.
Use two session ids and cookies — one HttpOnly, another not-HttpOnly. Both needs to be
present, to be logged in. By deleting the not-HttpOnly, one can log out, also if
the server is offline.  And, the HttpOnly session offers a bit extra security.

[serversid]
Server side sesison ids, maybe in Redis? So can log ppl out, without having access
to their browsers to delete session cookies.
(Also see [offline-first] above: need two cookies.)
Include session type (e.g. pwd, oauth, email secret, sso, emb sso) in sid:s,
see: SessionType.AutoTokenSiteCustomSso, and: SidOk(TySession.ApiSecretPart12, ..)
Also see: [weaksid]. [which_ws_session]
Add [sess_deleted_ix]
And [btr_sid].
Missing:  [forget_sid12] [restrict_sid_part_2_more] [bump_sid_last_use]

[sess_in_me]
Store xsrf token and any sid-part-1 in Me, instead of win.typs?

 
[hide_authn_btns]
If auto logged in via token in embedd*ing* html, then, don't show logout button
— instead the embedding site manages login/out for that user, by incl/excl the token.

[weaksid]

SECURITY Use HMAC. Not SHAX. Need not fix urgently. Prevents extension attacks

SECURITY Use Distroless? https://github.com/GoogleContainerTools/distroless instead of Alpine?
Slightly less attack surface, and slightly faster: glibc instead of musl.
https://news.ycombinator.com/item?id=19862002

SECURITY: https://news.ycombinator.com/item?id=19856419 "HTTP headers for the responsible developer"
https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity

SECURITY tests: https://github.com/beefproject/beef  ?
Also see:   [sec_tst_rscs] in sanitize-posts.2browsers.test.ts.

SECURITY  [br_authn_nonce]

SITE_PRIVACY  [avoid_glob_seq_nrs]
Generate publicly visible ids that aren't enumerable, e.g. let browser side visible
user ids and post ids, notification ids, etc, be short random strings, or
random 64 bit ints, rather than sequential numbers.
What notf ids and SMTP Message-ID to use instead? [older_notfs_emails]

[emb_login_req]
Make  login-required work also for embeded comments.

[emb_forum]
Make it possible to embedd a forum, or fourm cateory (no just blog comments).

[emb_ifr_shortcuts]
Make shortcuts work also if there're many iframes with blog comments comments
visible at the same time.
[many_ifr_my_page_data]
Make changing notf levels update the UI correctly also if many iframes.

[ty_oemb_og]
Add oEmbed & OpenGraph support to Ty.
See e.g.:
  https://wordpress.org/plugins/wpsso/
  https://blog.ycombinator.com/how-to-build-an-oembed-integration-for-your-startup-and-why-its-necessary/
And OpenGraph:
  https://wordpress.org/support/topic/social-sharing-on-linkedin-description-not-appearing/
LinkedIn likes oEmbed better than OpenGraph:
  https://surniaulula.com/2019/standards/og/linkedin-prefers-oembed-data-instead-of-open-graph/
(Commercial:  https://embed.ly  +  https://iframely.com,
iframely = OSS too:  https://github.com/itteco/iframely/tree/master/plugins/domains )

[like-notfs]  [like_vote_notfs]
Done: Send notifications about getting a Like.
Could also:
  Select which notifications get sent over email (e.g. excl Like votes & mod tasks).
  Still visible in username dropdown.

[do_notify_mod]
Generate a notification link, but no email, to a moderator who approves a reply to hanself?
So they can find & reply to that reply, also when han hanself is the one who approved the reply.

[dont_notify]
In some corner cases, one gets a notification about sth one already knows about,
can happen e.g. if adding a tag to a page one is subscribed to.

[notf_from_who]
E.g.  "You've been mentioned by @Alice in this comment by @Bob" — a notification is not
always by a single person? What if Bob wrote the comment, and Alice edited it and
added "@your_username", because she knows that Bob forgot to include your name in
a list of mentions? (Maybe Bob is a new hire.)
Or:  "You've been assigned by Alice to this task: Task Name created by Bob"  — Bob created
the task, and Alice assigned you.  Currently Ty mentions only Bob, but maybe Alice is
equally relevant?

[refactor-notfs]  DONE, except for re-enabling reconnect.
Remove Nchan. Use Rust + server-sent-events instead.  NO, just Play and WebSocket now.

[notf_schedule][snooze_schedule]
Individual prefs:
  Configure when auto-snoozing — e.g. weekends and before & after 0900-1700.
  Get someone's-waiting-for-approval notfs, if that person has been waiting
     for > X hours  *and* no other staff mem has reviewed yet.
  Or optionally if new member joins.

[external-server-js]
What? Same as [ext_markup_processor] ?

[ext_markup_processor]
Convert commonmark to html in external process / server, not in the JVM.
Works ok in the JVM, however, if importing a large site with thousands of posts to
process, that'd almost be a self-DoS. Better delegate to other server.

[rich-text-editor]
Later, support not only CommonMark but also rich text wysiwyg.
Then first need to fix: [DRAFTPRVW] ?

[html_json] [nashorn_in_tx] [save_post_lns_mentions]
Keep sanitized html in a json array with placeholders for site origin,
page titles (internal links), usernames, so the html can be reconstructed
"instantly" if the site origin changes, pages renamed, username changed.
And for different display resolutions — without having to re-render
CommonMark to html (which can be a bit slow).

[nashorn_in_tx]  COULD_OPTIMIZE
Do outside tx.

[double_tx]  COULD_OPTIMIZE
Read-only tx in tx is ok but unnecessary, a tiny bit slower & more mem.

[cache in tx] USE_StaleStuff_INSTEAD

[update_warmup_json]

COULD_OPTIMIZE
https://web.dev/content-visibility/
https://developer.mozilla.org/en-US/docs/Web/CSS/contain
Replace HashSet and HashMap w sth like C#'s HybridDictionary. [hybr_dict]
Can just search for HashSet and find lots of occurrences?

[macro-bug]
Find a better macro lib that won't "destroy" the file if @endif is on the line just above '}'.
Use the C preprocessor? Didn't find anything else that does what I'm looking for.

[devfriendly]
Maybe make all endpoints that accept, say, only POST, reply and tell other method
type invocations that they should use POST? Would need to change Play Fmw for this.

[React19_actions]
Can lots of React.useEffect() plus React.useRef() be replaced & simplified by
React 19's useActionState()?

[React_Router_v51]
Migrate unnecessary Route({ ... render: ({ match }) => ...) to hooks:
https://reacttraining.com/blog/react-router-v5-1/
But rename useParams to useUrlParams !?

[react_components]
Migrate from se https://mantine.dev/?

[webhooks_todo]
If endpoint seems broken, send emails to admins, after a while. [notf_adm_broken_webhook]
[bot_api]  [plugin]  [review_plugin]
Nice API for writing bots, e.g. chat bots or a Usability Testing Exchange external
server / bot.
Custom headers.
Sign.

[improve_imp_exp]
Remove fk from notfs_t to emails_out_t?

[per_pat_api_secrets]
Optional per ordinary user API secrets, e.g. personal bot?

[extensions] [themes]
Needs new table: t_extensions

Wow! Stylus <3  This:
    $uiHue = _subst_uiHue;
    $uiColor = _subst_hsl($uiHue 80% 50%);
    $uiColorAlt2 = _subst_hsl(_subst_uiHue2 70% 40%);
    body
      color: $uiColor;
      foo: 5px + 10
      foo: 2 ** 8
    div
      color: $uiColor;
      color: $uiColorAlt2;
      border: 1px solid $uiColor;
 —> this:
    body {
      color: _subst_hsl(_subst_uiHue 80% 50%);
      foo: 15px;
      foo: 256;
    }
    div {
      color: _subst_hsl(_subst_uiHue 80% 50%);
      color: _subst_hsl(_subst_uiHue2 70% 40%);
      border: 1px solid _subst_hsl(_subst_uiHue 80% 50%);
    }
  see:  https://stylus-lang.com/try.html#?code=%24uiHue%20%3D%20_subst_uiHue%3B%0A%24uiColor%20%3D%20_subst_hsl(%24uiHue%2080%25%2050%25)%3B%0A%0A%24uiColorAlt2%20%3D%20_subst_hsl(_subst_uiHue2%2070%25%2040%25)%3B%0A%0Abody%0A%20%20color%3A%20%24uiColor%3B%0A%20%20foo%3A%205px%20%2B%2010%0A%20%20foo%3A%202%20**%208%0A%0Adiv%0A%20%20color%3A%20%24uiColor%3B%0A%20%20color%3A%20%24uiColorAlt2%3B%0A%20%20border%3A%201px%20solid%20%24uiColor%3B
Then just Scala repl-all-literally, w custom colors & fonts ets, & @media (min-width: ...).


[site_conf_vals]
Generate target=_blank noopener links?
Generate https://host/page/path links, or /page/path links,  for in site rel links?


[nashorn_in_tx]  COULD_OPTIMIZE
Do outside tx.


[double_tx]  COULD_OPTIMIZE
Read-only tx in tx is ok but unnecessary, a tiny bit slower & more mem.


[Scala_213]
Try —> Using

[Scala_3]
Migration guide: https://github.com/scalacenter/scala-3-migration-guide

Typescript in the future?: [opaque_type]


[site_statistics]
ex data to incl: https://docs.mattermost.com/administration/statistics.html
   https://github.com/mattermost/mattermost-server/blob/master/app/analytics.go
     &  https://meta.discourse.org/u?period=weekly
   + stats:
      https://docs.mattermost.com/deployment/metrics.html#statistics
  ~= GitHub Contributors stats would be nice, per user / group?
   + stats per tag(s) / categories / users / groups.
       to see how a department or team (group) or type of topics
       (tags or category) is doing.
   Median and 90, 99 percentil response time? and Done [x] or Answered (v) time?
   Maybe as a function of when during the day, and on which day, topic posted?
   Both first reply response time (first reply to the Orig Post)
     and Followup replies response time, for replying to replies (until topic closed / done).
If wants very detailed: https://www.timescale.com?  PipelineDB?  Prometheus?
but then as a separate pg instance?
So a full dump of the actual site won't take forever because terrabytes time series data.
And cannot be 100% there are no bugs causing problems when running DDL data-def-lang migrations?
   "PostgreSQL instance was unrecoverably corrupted after the virtual machine running it was powered off"  https://dev.t-matix.com/blog/postgresql-as-a-time-series-database/
https://news.ycombinator.com/item?id=20760324
TimescaleDB = pg extentsion, nice, no need to learn a completely new db & storage type.

[use_pingora]
Some time after Pingora has been open sourced, stop using Nginx and Lua,
start using Pingora instead (for performance, security and developer friendliness reasons).
https://blog.cloudflare.com/how-we-built-pingora-the-proxy-that-connects-cloudflare-to-the-internet/
via: https://news.ycombinator.com/item?id=32836661

[lua_conf]  SKIP, instead [use_pingora]
Conf settings for Lua plugins.

[typesafe_lua]  SKIP, instead [use_pingora]
Maybe: https://github.com/teal-language/tl  Teal, a typed dialect of Lua  ?
  by https://github.com/hishamhm  works at Kong. FOSDEM2020, VSCode & Vim support.
https://github.com/emilk/sol — no longer in active development

[blake3]  COULD_OPTIMIZE
Blake3 is ~ 10x faster than SHA2:
  https://news.ycombinator.com/item?id=22021769
  https://news.ycombinator.com/item?id=22003315
  Java bindings:
    https://dev.to/johanneslichtenberger/blake3-crytopgrahic-hash-function-java-implementation-2bg7
    —>  https://github.com/sken77/BLAKE3jni
Wait until there's a well tested Java lib available?
Then use Blake3 instead e.g. to hash long Redis keys (and just delete the old
cached things, it's a cache).

[ix_big_pgs][do_not_index]
Should big pages with 1000? 10 000? comments — typically chat channels — be reindexed
if e.g. opened/closed,  or moved to a different category?
Elsewhere: In 1) Slack, 2) Teams, 3) Mattermost and 4) Zulip, there aren't forum categories,
    and you cannot label/tag chat channels. There's a feature request in Zulip about
    tagging individual messages: https://github.com/zulip/zulip/issues/1901.
    In 5) Discourse, it seems one can't search chat channels: (as of Feb -24)
    https://meta.discourse.org/t/ability-to-search-messages-in-chat/240803/19.
    In 6) RocketChat, Omnichannel conversations can be tagged — but they're typically shorter
    one-to-one conversations?
    So, none of those needs to think about this.
In Ty, alt 1) Chat channels cannot be moved to other categories, if they're
too long? Instead one creates a new chat?. Which can make sense, because if there are
1000 messages, it can be risky to move it to a category with possibly different
access permissions? (Maybe there's something private in the 1000 previous messages?)
And maybe chat messages shouldn't be found via "is:open/closed",
only the chat itself? And a chat cannot be tagged itself, only individual messages in
the chat? Alt 2) There could be a message that it'll take an hour
until the chat has been reindexed? Combined with extra debounce / rate limiting?
Alt 3) A combination of 1 & 2. Alt 4) ?

[ix_unappr]
Index plain text for these too, nice for mods to search & find?  In an [inactive_index]?

[search_hmm]
Surprising search behavior:
  - Only exact category matches (ignores sub cats).
  - Only exact tag matches too [how_ix_page_tags]  (not comments on a tagged page).
  - "is:aa,bb,cc" is incorrectly interpreted as  "is:aa is:bb is:cc".

[rebuild_reindex_posts]
A background job that regenerates html (CommonMark –> html) and
renindexes all posts (or all posts in a category / tag / by certain participants).
It finds all links/backlinks, upload refs, ext link preview links,
changes any  embeddedOriginOrEmpty links,  and any url subfolder installation path,
& full text search reindexes,
Without! any long running db transactions. 1) loads the data it needs, in a read-only tx.
Then, compiles Commonmark to html, finds all links etc.
Then, a read-write-tx: If the post wasn't changed since the r/o tx, then
save the rebuilt post.
Also, when upgrading ElasticSearch, maybe reindex everything and improve the mapping:
Change ids, e.g. site & category id, from type integer to type keword.  [es_kwd]

[x_plat_offl_builds]
Offline builds on various Linux distros, not just Debian 10?


[ty_v1] Things to fix before Ty stack version 1: 0.20YY.NR —> version 1.20YY.NR

Ty v1 is a major release. Server admins will be required to install Ty v1
on a new server, and verify that it works — before shutting down one's Ty v0.
So, it'll be OK to make changes in Ty that otherwise would have been deemed too risky.

Ty v1 removes some deprecated things, e.g.: Remove class ed.server.EdAppLoader (everyone
needs to remove `play.application.loader= ed.server.EdAppLoader` from the Play conf file)

Docker volumes instead of bind mounts, see e.g.:
  https://stackoverflow.com/questions/23544282/what-is-the-best-way-to-manage-permissions-for-docker-shared-volumes

Rename Silhouette authn settings to talkyard.authn.____  instead?
Require that the old conf vals be removed (log helpful errors.)

s/typ.ts script: Check mem before upgrading, warn if too little? balloon.txt file?

Use firewalld instead of ufw  [firewalld_not_ufw]
    - recommended by the Debian wiki: https://wiki.debian.org/DebianFirewall
    - better than ufw; ufw won't block exposed Docker ports,
      see:  https://github.com/chaifeng/ufw-docker
  Sth like:
  firewall-cmd --zone=public --permanent --add-service=http
  firewall-cmd --zone=public --permanent --add-service=https


? Rename /opt/talkyard/conf/play-framework.conf  to  talkyard-app.conf
so can use other tech than only Play Fmw, without the file name getting misleading.
(There're HOCON file format parsers in rust and js too, not only Scala.)

/opt/talkyard/
    .git/
    env-vars —> .env
        # in env-vars file:
        CURRENT_VERSION=...
        PINNED_VERSION=...
    docker-compose.yml —> versions/docker-compose.yml

    version/
        .git/
        docker-compose.yml

    conf/
        app/
            play-framework.conf
        web/
            nginx-server-blocks.conf
        acme/
            acme-account.key   [ty_v1] generate on the host instead so can be :ro?
        rdb/
            postgresql.conf
        ...
    volumes/
        app/
        cache/
        rdb/
        ...
    docs/
    old/
        versions/
            v1.2021.05/
              env-vars
              docker-compose.yml
              backup-archives-paths.txt

/opt/talkyard-backups/
    archives/
        ...


[ai_spam_checks]
Use AI (LLMs) to detect spam. Include in the AI prompt:
    - The description of the forum — can use the forum intro text?
    - The description of the current forum category.
    - The text in the parent and ancestor comments, back up to
      the Original Post & title. (If the thread is long, maybe just
      the OP and parent comment.)
    - The addresses to one's own websites (domains),
      and brief descriptions about them.
    - The post author:
        - For how long they've been a member,
        - their trust level,
        - how many OK posts they've posted before, how many spam posts.


[bash2deno][ty_v1]
Port scripts from Bash to Deno.


[release_channels][ty_v1] [mv_2_adm_docs]
Kubernetes has release channels:
  Rapid, Regular (the default), Stable — looks nice
  (details: https://cloud.google.com/kubernetes-engine/docs/concepts/release-channels).


[todoc]
Write docs — search for `[todoc`, find e.g. `[todoc_certs]`.
About how to write docs:
- https://documentation.divio.com:
      Four kinds: tutorials, how-to guides, technical reference and explanation
      Related discussion: https://news.ycombinator.com/item?id=26002656
- https://v3.vuejs.org/guide/contributing/writing-guide.html:
      Vuejs writing guide


[ios_bugs]
Problems that happen only on iOS Safari (and Chrome, but all browsers are basically
Safari on iOS). When works elsewhere, this might be bugs in iOS Safari.

[ios_itp]
Trigger happy tracking prevention produces problems.
Maybe maybe Safari [ios_itp] will sometimes mistake email verification links
or SSO url params, for being click trackers — e.g. Talkyard's
#talkyardOneTimeLoginSecret=... hash fragment param. See:
https://webkit.org/blog/9521/intelligent-tracking-prevention-2-3/
— then, Safari would delete the Ty session parts 1+2 from localStorage,
after a day.
https://www.cookiestatus.com/safari/

[default_plugins]
KaTeX for Maths (VSCode supports KaTeX, and Docusaurus too:
https://docusaurus.io/docs/markdown-features/math-equations )
Prism for syntax highlighting (Docusaurus uses it:
https://docusaurus.io/docs/markdown-features/code-blocks#syntax-highlighting )


[wdio_6_to_7]
Upgr Webdriverio from 6 to 7.
Add Typescript-ESLint warning: no-floating-promises!

[java_8_to_11] [slim_jre]


Better troubleshooting
=====
[better_logging]

Bugs
=====
Can double submit a reply by double clicking Ctrl+Enter. Then, error TyE6QSADTH04
happens.

Select an answer or solution to a question / problem. Then, change topic type to Discussion.
Then back to Question or Problem. Now, in the topic list, the icon will incorrectly be
the *unsolved* question or problem — although the answer is still selected (after
having changed the topic type back) and the page is solved.
