For now? I'm experimenting on an old concept.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

main branch coming back after divergent branches all over the place

MLC Bloeiman 61078d86

+1545
+21
.dockerignore
··· 1 + # Ignore git and build files 2 + .git 3 + .gitignore 4 + *.log 5 + *.tmp 6 + *.swp 7 + *.swo 8 + *.bak 9 + *.DS_Store 10 + 11 + # Editor/IDE files 12 + .vscode/ 13 + .idea/ 14 + 15 + # Misc 16 + *.env.local 17 + .envrc 18 + .env.*.local 19 + 20 + docker/ 21 + data/
+696
.editorconfig
··· 1 + # EditorConfig is awesome: https://EditorConfig.org 2 + # top-most EditorConfig file 3 + root = true 4 + 5 + [*] 6 + indent_style = tab 7 + indent_size = 4 8 + end_of_line = lf 9 + charset = utf-8 10 + trim_trailing_whitespace = true 11 + insert_final_newline = true 12 + max_line_length = 120 13 + tab_width = 4 14 + ij_continuation_indent_size = 8 15 + ij_formatter_off_tag = @formatter:off 16 + ij_formatter_on_tag = @formatter:on 17 + ij_formatter_tags_enabled = true 18 + ij_smart_tabs = false 19 + ij_visual_guides = 20 + ij_wrap_on_typing = false 21 + 22 + [*.css] 23 + ij_css_align_closing_brace_with_properties = false 24 + ij_css_blank_lines_around_nested_selector = 1 25 + ij_css_blank_lines_between_blocks = 1 26 + ij_css_block_comment_add_space = false 27 + ij_css_brace_placement = end_of_line 28 + ij_css_enforce_quotes_on_format = false 29 + ij_css_hex_color_long_format = false 30 + ij_css_hex_color_lower_case = false 31 + ij_css_hex_color_short_format = false 32 + ij_css_hex_color_upper_case = false 33 + ij_css_keep_blank_lines_in_code = 2 34 + ij_css_keep_indents_on_empty_lines = false 35 + ij_css_keep_single_line_blocks = false 36 + ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow 37 + ij_css_space_after_colon = true 38 + ij_css_space_before_opening_brace = true 39 + ij_css_use_double_quotes = true 40 + ij_css_value_alignment = do_not_align 41 + 42 + [*.rs] 43 + max_line_length = 100 44 + ij_continuation_indent_size = 4 45 + ij_rust_align_multiline_chained_methods = false 46 + ij_rust_align_multiline_parameters = true 47 + ij_rust_align_multiline_parameters_in_calls = true 48 + ij_rust_align_ret_type = true 49 + ij_rust_align_type_params = false 50 + ij_rust_align_where_bounds = true 51 + ij_rust_align_where_clause = false 52 + ij_rust_allow_one_line_match = false 53 + ij_rust_block_comment_at_first_column = false 54 + ij_rust_do_not_format_rustfmt_skip = true 55 + ij_rust_indent_where_clause = false 56 + ij_rust_keep_blank_lines_in_code = 2 57 + ij_rust_keep_blank_lines_in_declarations = 2 58 + ij_rust_keep_first_column_comment = false 59 + ij_rust_keep_indents_on_empty_lines = false 60 + ij_rust_keep_line_breaks = true 61 + ij_rust_line_comment_add_space = true 62 + ij_rust_line_comment_at_first_column = false 63 + ij_rust_max_number_of_blanks_between_field_patterns = 1 64 + ij_rust_max_number_of_blanks_between_value_parameters = 1 65 + ij_rust_min_number_of_blanks_between_items = 0 66 + ij_rust_new_line_after_where = true 67 + ij_rust_preserve_end_of_line_comments_on_the_same_line = true 68 + ij_rust_preserve_punctuation = false 69 + ij_rust_spaces_around_assoc_type_binding = false 70 + ij_rust_where_on_new_line = true 71 + 72 + [.editorconfig] 73 + ij_editorconfig_align_group_field_declarations = false 74 + ij_editorconfig_space_after_colon = false 75 + ij_editorconfig_space_after_comma = true 76 + ij_editorconfig_space_before_colon = false 77 + ij_editorconfig_space_before_comma = false 78 + ij_editorconfig_spaces_around_assignment_operators = true 79 + 80 + [{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wadl,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}] 81 + ij_xml_align_attributes = true 82 + ij_xml_align_text = false 83 + ij_xml_attribute_wrap = normal 84 + ij_xml_block_comment_add_space = false 85 + ij_xml_block_comment_at_first_column = true 86 + ij_xml_keep_blank_lines = 2 87 + ij_xml_keep_indents_on_empty_lines = false 88 + ij_xml_keep_line_breaks = true 89 + ij_xml_keep_line_breaks_in_text = true 90 + ij_xml_keep_whitespaces = false 91 + ij_xml_keep_whitespaces_around_cdata = preserve 92 + ij_xml_keep_whitespaces_inside_cdata = false 93 + ij_xml_line_comment_at_first_column = true 94 + ij_xml_space_after_tag_name = false 95 + ij_xml_space_around_equals_in_attribute = false 96 + ij_xml_space_inside_empty_tag = false 97 + ij_xml_text_wrap = normal 98 + 99 + [{*.ats,*.cts,*.mts,*.ts}] 100 + ij_continuation_indent_size = 4 101 + ij_typescript_align_imports = false 102 + ij_typescript_align_multiline_array_initializer_expression = false 103 + ij_typescript_align_multiline_binary_operation = false 104 + ij_typescript_align_multiline_chained_methods = false 105 + ij_typescript_align_multiline_extends_list = false 106 + ij_typescript_align_multiline_for = true 107 + ij_typescript_align_multiline_parameters = true 108 + ij_typescript_align_multiline_parameters_in_calls = false 109 + ij_typescript_align_multiline_ternary_operation = false 110 + ij_typescript_align_object_properties = 0 111 + ij_typescript_align_union_types = false 112 + ij_typescript_align_var_statements = 0 113 + ij_typescript_array_initializer_new_line_after_left_brace = false 114 + ij_typescript_array_initializer_right_brace_on_new_line = false 115 + ij_typescript_array_initializer_wrap = off 116 + ij_typescript_assignment_wrap = off 117 + ij_typescript_binary_operation_sign_on_next_line = false 118 + ij_typescript_binary_operation_wrap = off 119 + ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** 120 + ij_typescript_blank_lines_after_imports = 1 121 + ij_typescript_blank_lines_around_class = 1 122 + ij_typescript_blank_lines_around_field = 0 123 + ij_typescript_blank_lines_around_field_in_interface = 0 124 + ij_typescript_blank_lines_around_function = 1 125 + ij_typescript_blank_lines_around_method = 1 126 + ij_typescript_blank_lines_around_method_in_interface = 1 127 + ij_typescript_block_brace_style = end_of_line 128 + ij_typescript_block_comment_add_space = false 129 + ij_typescript_block_comment_at_first_column = true 130 + ij_typescript_call_parameters_new_line_after_left_paren = false 131 + ij_typescript_call_parameters_right_paren_on_new_line = false 132 + ij_typescript_call_parameters_wrap = off 133 + ij_typescript_catch_on_new_line = false 134 + ij_typescript_chained_call_dot_on_new_line = true 135 + ij_typescript_class_brace_style = end_of_line 136 + ij_typescript_class_decorator_wrap = split_into_lines 137 + ij_typescript_class_field_decorator_wrap = off 138 + ij_typescript_class_method_decorator_wrap = off 139 + ij_typescript_comma_on_new_line = false 140 + ij_typescript_do_while_brace_force = never 141 + ij_typescript_else_on_new_line = false 142 + ij_typescript_enforce_trailing_comma = keep 143 + ij_typescript_enum_constants_wrap = on_every_item 144 + ij_typescript_extends_keyword_wrap = off 145 + ij_typescript_extends_list_wrap = off 146 + ij_typescript_field_prefix = _ 147 + ij_typescript_file_name_style = relaxed 148 + ij_typescript_finally_on_new_line = false 149 + ij_typescript_for_brace_force = never 150 + ij_typescript_for_statement_new_line_after_left_paren = false 151 + ij_typescript_for_statement_right_paren_on_new_line = false 152 + ij_typescript_for_statement_wrap = off 153 + ij_typescript_force_quote_style = false 154 + ij_typescript_force_semicolon_style = false 155 + ij_typescript_function_expression_brace_style = end_of_line 156 + ij_typescript_function_parameter_decorator_wrap = off 157 + ij_typescript_if_brace_force = never 158 + ij_typescript_import_merge_members = global 159 + ij_typescript_import_prefer_absolute_path = global 160 + ij_typescript_import_sort_members = true 161 + ij_typescript_import_sort_module_name = false 162 + ij_typescript_import_use_node_resolution = true 163 + ij_typescript_imports_wrap = on_every_item 164 + ij_typescript_indent_case_from_switch = true 165 + ij_typescript_indent_chained_calls = true 166 + ij_typescript_indent_package_children = 0 167 + ij_typescript_jsdoc_include_types = false 168 + ij_typescript_jsx_attribute_value = braces 169 + ij_typescript_keep_blank_lines_in_code = 2 170 + ij_typescript_keep_first_column_comment = true 171 + ij_typescript_keep_indents_on_empty_lines = false 172 + ij_typescript_keep_line_breaks = true 173 + ij_typescript_keep_simple_blocks_in_one_line = false 174 + ij_typescript_keep_simple_methods_in_one_line = false 175 + ij_typescript_line_comment_add_space = true 176 + ij_typescript_line_comment_at_first_column = false 177 + ij_typescript_method_brace_style = end_of_line 178 + ij_typescript_method_call_chain_wrap = off 179 + ij_typescript_method_parameters_new_line_after_left_paren = false 180 + ij_typescript_method_parameters_right_paren_on_new_line = false 181 + ij_typescript_method_parameters_wrap = off 182 + ij_typescript_object_literal_wrap = on_every_item 183 + ij_typescript_object_types_wrap = on_every_item 184 + ij_typescript_parentheses_expression_new_line_after_left_paren = false 185 + ij_typescript_parentheses_expression_right_paren_on_new_line = false 186 + ij_typescript_place_assignment_sign_on_next_line = false 187 + ij_typescript_prefer_as_type_cast = false 188 + ij_typescript_prefer_explicit_types_function_expression_returns = false 189 + ij_typescript_prefer_explicit_types_function_returns = false 190 + ij_typescript_prefer_explicit_types_vars_fields = false 191 + ij_typescript_prefer_parameters_wrap = false 192 + ij_typescript_property_prefix = 193 + ij_typescript_reformat_c_style_comments = false 194 + ij_typescript_space_after_colon = true 195 + ij_typescript_space_after_comma = true 196 + ij_typescript_space_after_dots_in_rest_parameter = false 197 + ij_typescript_space_after_generator_mult = true 198 + ij_typescript_space_after_property_colon = true 199 + ij_typescript_space_after_quest = true 200 + ij_typescript_space_after_type_colon = true 201 + ij_typescript_space_after_unary_not = false 202 + ij_typescript_space_before_async_arrow_lparen = true 203 + ij_typescript_space_before_catch_keyword = true 204 + ij_typescript_space_before_catch_left_brace = true 205 + ij_typescript_space_before_catch_parentheses = true 206 + ij_typescript_space_before_class_lbrace = true 207 + ij_typescript_space_before_class_left_brace = true 208 + ij_typescript_space_before_colon = true 209 + ij_typescript_space_before_comma = false 210 + ij_typescript_space_before_do_left_brace = true 211 + ij_typescript_space_before_else_keyword = true 212 + ij_typescript_space_before_else_left_brace = true 213 + ij_typescript_space_before_finally_keyword = true 214 + ij_typescript_space_before_finally_left_brace = true 215 + ij_typescript_space_before_for_left_brace = true 216 + ij_typescript_space_before_for_parentheses = true 217 + ij_typescript_space_before_for_semicolon = false 218 + ij_typescript_space_before_function_left_parenth = true 219 + ij_typescript_space_before_generator_mult = false 220 + ij_typescript_space_before_if_left_brace = true 221 + ij_typescript_space_before_if_parentheses = true 222 + ij_typescript_space_before_method_call_parentheses = false 223 + ij_typescript_space_before_method_left_brace = true 224 + ij_typescript_space_before_method_parentheses = false 225 + ij_typescript_space_before_property_colon = false 226 + ij_typescript_space_before_quest = true 227 + ij_typescript_space_before_switch_left_brace = true 228 + ij_typescript_space_before_switch_parentheses = true 229 + ij_typescript_space_before_try_left_brace = true 230 + ij_typescript_space_before_type_colon = false 231 + ij_typescript_space_before_unary_not = false 232 + ij_typescript_space_before_while_keyword = true 233 + ij_typescript_space_before_while_left_brace = true 234 + ij_typescript_space_before_while_parentheses = true 235 + ij_typescript_spaces_around_additive_operators = true 236 + ij_typescript_spaces_around_arrow_function_operator = true 237 + ij_typescript_spaces_around_assignment_operators = true 238 + ij_typescript_spaces_around_bitwise_operators = true 239 + ij_typescript_spaces_around_equality_operators = true 240 + ij_typescript_spaces_around_logical_operators = true 241 + ij_typescript_spaces_around_multiplicative_operators = true 242 + ij_typescript_spaces_around_relational_operators = true 243 + ij_typescript_spaces_around_shift_operators = true 244 + ij_typescript_spaces_around_unary_operator = false 245 + ij_typescript_spaces_within_array_initializer_brackets = false 246 + ij_typescript_spaces_within_brackets = false 247 + ij_typescript_spaces_within_catch_parentheses = false 248 + ij_typescript_spaces_within_for_parentheses = false 249 + ij_typescript_spaces_within_if_parentheses = false 250 + ij_typescript_spaces_within_imports = false 251 + ij_typescript_spaces_within_interpolation_expressions = false 252 + ij_typescript_spaces_within_method_call_parentheses = false 253 + ij_typescript_spaces_within_method_parentheses = false 254 + ij_typescript_spaces_within_object_literal_braces = false 255 + ij_typescript_spaces_within_object_type_braces = true 256 + ij_typescript_spaces_within_parentheses = false 257 + ij_typescript_spaces_within_switch_parentheses = false 258 + ij_typescript_spaces_within_type_assertion = false 259 + ij_typescript_spaces_within_union_types = true 260 + ij_typescript_spaces_within_while_parentheses = false 261 + ij_typescript_special_else_if_treatment = true 262 + ij_typescript_ternary_operation_signs_on_next_line = false 263 + ij_typescript_ternary_operation_wrap = off 264 + ij_typescript_union_types_wrap = on_every_item 265 + ij_typescript_use_chained_calls_group_indents = false 266 + ij_typescript_use_double_quotes = true 267 + ij_typescript_use_explicit_js_extension = auto 268 + ij_typescript_use_import_type = auto 269 + ij_typescript_use_path_mapping = always 270 + ij_typescript_use_public_modifier = false 271 + ij_typescript_use_semicolon_after_statement = true 272 + ij_typescript_var_declaration_wrap = normal 273 + ij_typescript_while_brace_force = never 274 + ij_typescript_while_on_new_line = false 275 + ij_typescript_wrap_comments = false 276 + 277 + [{*.cjs,*.es6,*.js,*.mjs}] 278 + ij_continuation_indent_size = 4 279 + ij_javascript_align_imports = false 280 + ij_javascript_align_multiline_array_initializer_expression = false 281 + ij_javascript_align_multiline_binary_operation = false 282 + ij_javascript_align_multiline_chained_methods = false 283 + ij_javascript_align_multiline_extends_list = false 284 + ij_javascript_align_multiline_for = true 285 + ij_javascript_align_multiline_parameters = true 286 + ij_javascript_align_multiline_parameters_in_calls = false 287 + ij_javascript_align_multiline_ternary_operation = false 288 + ij_javascript_align_object_properties = 0 289 + ij_javascript_align_union_types = false 290 + ij_javascript_align_var_statements = 0 291 + ij_javascript_array_initializer_new_line_after_left_brace = false 292 + ij_javascript_array_initializer_right_brace_on_new_line = false 293 + ij_javascript_array_initializer_wrap = off 294 + ij_javascript_assignment_wrap = off 295 + ij_javascript_binary_operation_sign_on_next_line = false 296 + ij_javascript_binary_operation_wrap = off 297 + ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** 298 + ij_javascript_blank_lines_after_imports = 1 299 + ij_javascript_blank_lines_around_class = 1 300 + ij_javascript_blank_lines_around_field = 0 301 + ij_javascript_blank_lines_around_function = 1 302 + ij_javascript_blank_lines_around_method = 1 303 + ij_javascript_block_brace_style = end_of_line 304 + ij_javascript_block_comment_add_space = false 305 + ij_javascript_block_comment_at_first_column = true 306 + ij_javascript_call_parameters_new_line_after_left_paren = false 307 + ij_javascript_call_parameters_right_paren_on_new_line = false 308 + ij_javascript_call_parameters_wrap = off 309 + ij_javascript_catch_on_new_line = false 310 + ij_javascript_chained_call_dot_on_new_line = true 311 + ij_javascript_class_brace_style = end_of_line 312 + ij_javascript_class_decorator_wrap = split_into_lines 313 + ij_javascript_class_field_decorator_wrap = off 314 + ij_javascript_class_method_decorator_wrap = off 315 + ij_javascript_comma_on_new_line = false 316 + ij_javascript_do_while_brace_force = never 317 + ij_javascript_else_on_new_line = false 318 + ij_javascript_enforce_trailing_comma = keep 319 + ij_javascript_extends_keyword_wrap = off 320 + ij_javascript_extends_list_wrap = off 321 + ij_javascript_field_prefix = _ 322 + ij_javascript_file_name_style = relaxed 323 + ij_javascript_finally_on_new_line = false 324 + ij_javascript_for_brace_force = never 325 + ij_javascript_for_statement_new_line_after_left_paren = false 326 + ij_javascript_for_statement_right_paren_on_new_line = false 327 + ij_javascript_for_statement_wrap = off 328 + ij_javascript_force_quote_style = false 329 + ij_javascript_force_semicolon_style = false 330 + ij_javascript_function_expression_brace_style = end_of_line 331 + ij_javascript_function_parameter_decorator_wrap = off 332 + ij_javascript_if_brace_force = never 333 + ij_javascript_import_merge_members = global 334 + ij_javascript_import_prefer_absolute_path = global 335 + ij_javascript_import_sort_members = true 336 + ij_javascript_import_sort_module_name = false 337 + ij_javascript_import_use_node_resolution = true 338 + ij_javascript_imports_wrap = on_every_item 339 + ij_javascript_indent_case_from_switch = true 340 + ij_javascript_indent_chained_calls = true 341 + ij_javascript_indent_package_children = 0 342 + ij_javascript_jsx_attribute_value = braces 343 + ij_javascript_keep_blank_lines_in_code = 2 344 + ij_javascript_keep_first_column_comment = true 345 + ij_javascript_keep_indents_on_empty_lines = false 346 + ij_javascript_keep_line_breaks = true 347 + ij_javascript_keep_simple_blocks_in_one_line = false 348 + ij_javascript_keep_simple_methods_in_one_line = false 349 + ij_javascript_line_comment_add_space = true 350 + ij_javascript_line_comment_at_first_column = false 351 + ij_javascript_method_brace_style = end_of_line 352 + ij_javascript_method_call_chain_wrap = off 353 + ij_javascript_method_parameters_new_line_after_left_paren = false 354 + ij_javascript_method_parameters_right_paren_on_new_line = false 355 + ij_javascript_method_parameters_wrap = off 356 + ij_javascript_object_literal_wrap = on_every_item 357 + ij_javascript_object_types_wrap = on_every_item 358 + ij_javascript_parentheses_expression_new_line_after_left_paren = false 359 + ij_javascript_parentheses_expression_right_paren_on_new_line = false 360 + ij_javascript_place_assignment_sign_on_next_line = false 361 + ij_javascript_prefer_as_type_cast = false 362 + ij_javascript_prefer_explicit_types_function_expression_returns = false 363 + ij_javascript_prefer_explicit_types_function_returns = false 364 + ij_javascript_prefer_explicit_types_vars_fields = false 365 + ij_javascript_prefer_parameters_wrap = false 366 + ij_javascript_property_prefix = 367 + ij_javascript_reformat_c_style_comments = false 368 + ij_javascript_space_after_colon = true 369 + ij_javascript_space_after_comma = true 370 + ij_javascript_space_after_dots_in_rest_parameter = false 371 + ij_javascript_space_after_generator_mult = true 372 + ij_javascript_space_after_property_colon = true 373 + ij_javascript_space_after_quest = true 374 + ij_javascript_space_after_type_colon = true 375 + ij_javascript_space_after_unary_not = false 376 + ij_javascript_space_before_async_arrow_lparen = true 377 + ij_javascript_space_before_catch_keyword = true 378 + ij_javascript_space_before_catch_left_brace = true 379 + ij_javascript_space_before_catch_parentheses = true 380 + ij_javascript_space_before_class_lbrace = true 381 + ij_javascript_space_before_class_left_brace = true 382 + ij_javascript_space_before_colon = true 383 + ij_javascript_space_before_comma = false 384 + ij_javascript_space_before_do_left_brace = true 385 + ij_javascript_space_before_else_keyword = true 386 + ij_javascript_space_before_else_left_brace = true 387 + ij_javascript_space_before_finally_keyword = true 388 + ij_javascript_space_before_finally_left_brace = true 389 + ij_javascript_space_before_for_left_brace = true 390 + ij_javascript_space_before_for_parentheses = true 391 + ij_javascript_space_before_for_semicolon = false 392 + ij_javascript_space_before_function_left_parenth = true 393 + ij_javascript_space_before_generator_mult = false 394 + ij_javascript_space_before_if_left_brace = true 395 + ij_javascript_space_before_if_parentheses = true 396 + ij_javascript_space_before_method_call_parentheses = false 397 + ij_javascript_space_before_method_left_brace = true 398 + ij_javascript_space_before_method_parentheses = false 399 + ij_javascript_space_before_property_colon = false 400 + ij_javascript_space_before_quest = true 401 + ij_javascript_space_before_switch_left_brace = true 402 + ij_javascript_space_before_switch_parentheses = true 403 + ij_javascript_space_before_try_left_brace = true 404 + ij_javascript_space_before_type_colon = false 405 + ij_javascript_space_before_unary_not = false 406 + ij_javascript_space_before_while_keyword = true 407 + ij_javascript_space_before_while_left_brace = true 408 + ij_javascript_space_before_while_parentheses = true 409 + ij_javascript_spaces_around_additive_operators = true 410 + ij_javascript_spaces_around_arrow_function_operator = true 411 + ij_javascript_spaces_around_assignment_operators = true 412 + ij_javascript_spaces_around_bitwise_operators = true 413 + ij_javascript_spaces_around_equality_operators = true 414 + ij_javascript_spaces_around_logical_operators = true 415 + ij_javascript_spaces_around_multiplicative_operators = true 416 + ij_javascript_spaces_around_relational_operators = true 417 + ij_javascript_spaces_around_shift_operators = true 418 + ij_javascript_spaces_around_unary_operator = false 419 + ij_javascript_spaces_within_array_initializer_brackets = false 420 + ij_javascript_spaces_within_brackets = false 421 + ij_javascript_spaces_within_catch_parentheses = false 422 + ij_javascript_spaces_within_for_parentheses = false 423 + ij_javascript_spaces_within_if_parentheses = false 424 + ij_javascript_spaces_within_imports = false 425 + ij_javascript_spaces_within_interpolation_expressions = false 426 + ij_javascript_spaces_within_method_call_parentheses = false 427 + ij_javascript_spaces_within_method_parentheses = false 428 + ij_javascript_spaces_within_object_literal_braces = false 429 + ij_javascript_spaces_within_object_type_braces = true 430 + ij_javascript_spaces_within_parentheses = false 431 + ij_javascript_spaces_within_switch_parentheses = false 432 + ij_javascript_spaces_within_type_assertion = false 433 + ij_javascript_spaces_within_union_types = true 434 + ij_javascript_spaces_within_while_parentheses = false 435 + ij_javascript_special_else_if_treatment = true 436 + ij_javascript_ternary_operation_signs_on_next_line = false 437 + ij_javascript_ternary_operation_wrap = off 438 + ij_javascript_union_types_wrap = on_every_item 439 + ij_javascript_use_chained_calls_group_indents = false 440 + ij_javascript_use_double_quotes = true 441 + ij_javascript_use_explicit_js_extension = auto 442 + ij_javascript_use_import_type = auto 443 + ij_javascript_use_path_mapping = always 444 + ij_javascript_use_public_modifier = false 445 + ij_javascript_use_semicolon_after_statement = true 446 + ij_javascript_var_declaration_wrap = normal 447 + ij_javascript_while_brace_force = never 448 + ij_javascript_while_on_new_line = false 449 + ij_javascript_wrap_comments = false 450 + 451 + [{*.har,*.jsb2,*.jsb3,*.json,*.jsonc,*.postman_collection,*.postman_environment,.babelrc,.eslintrc,.prettierrc,.stylelintrc,.ws-context,jest.config}] 452 + indent_size = 2 453 + ij_json_array_wrapping = split_into_lines 454 + ij_json_keep_blank_lines_in_code = 0 455 + ij_json_keep_indents_on_empty_lines = false 456 + ij_json_keep_line_breaks = true 457 + ij_json_keep_trailing_comma = false 458 + ij_json_object_wrapping = split_into_lines 459 + ij_json_property_alignment = do_not_align 460 + ij_json_space_after_colon = true 461 + ij_json_space_after_comma = true 462 + ij_json_space_before_colon = false 463 + ij_json_space_before_comma = false 464 + ij_json_spaces_within_braces = false 465 + ij_json_spaces_within_brackets = false 466 + ij_json_wrap_long_lines = false 467 + 468 + [{*.htm,*.html,*.sht,*.shtm,*.shtml}] 469 + ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3 470 + ij_html_align_attributes = true 471 + ij_html_align_text = false 472 + ij_html_attribute_wrap = normal 473 + ij_html_block_comment_add_space = false 474 + ij_html_block_comment_at_first_column = true 475 + ij_html_do_not_align_children_of_min_lines = 0 476 + ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p 477 + ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot 478 + ij_html_enforce_quotes = false 479 + ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var 480 + ij_html_keep_blank_lines = 2 481 + ij_html_keep_indents_on_empty_lines = false 482 + ij_html_keep_line_breaks = true 483 + ij_html_keep_line_breaks_in_text = true 484 + ij_html_keep_whitespaces = false 485 + ij_html_keep_whitespaces_inside = span,pre,textarea 486 + ij_html_line_comment_at_first_column = true 487 + ij_html_new_line_after_last_attribute = never 488 + ij_html_new_line_before_first_attribute = never 489 + ij_html_quote_style = double 490 + ij_html_remove_new_line_before_tags = br 491 + ij_html_space_after_tag_name = false 492 + ij_html_space_around_equality_in_attribute = false 493 + ij_html_space_inside_empty_tag = false 494 + ij_html_text_wrap = normal 495 + 496 + [{*.http,*.rest}] 497 + ij_continuation_indent_size = 4 498 + ij_http-request_call_parameters_wrap = normal 499 + ij_http-request_method_parameters_wrap = split_into_lines 500 + ij_http-request_space_before_comma = true 501 + ij_http-request_spaces_around_assignment_operators = true 502 + 503 + [{*.markdown,*.md}] 504 + ij_markdown_force_one_space_after_blockquote_symbol = true 505 + ij_markdown_force_one_space_after_header_symbol = true 506 + ij_markdown_force_one_space_after_list_bullet = true 507 + ij_markdown_force_one_space_between_words = true 508 + ij_markdown_format_tables = true 509 + ij_markdown_insert_quote_arrows_on_wrap = true 510 + ij_markdown_keep_indents_on_empty_lines = false 511 + ij_markdown_keep_line_breaks_inside_text_blocks = true 512 + ij_markdown_max_lines_around_block_elements = 1 513 + ij_markdown_max_lines_around_header = 1 514 + ij_markdown_max_lines_between_paragraphs = 1 515 + ij_markdown_min_lines_around_block_elements = 1 516 + ij_markdown_min_lines_around_header = 1 517 + ij_markdown_min_lines_between_paragraphs = 1 518 + ij_markdown_wrap_text_if_long = true 519 + ij_markdown_wrap_text_inside_blockquotes = true 520 + 521 + [{*.qml,*.qmltypes}] 522 + ij_continuation_indent_size = 4 523 + ij_qmllang_align_imports = false 524 + ij_qmllang_align_multiline_array_initializer_expression = false 525 + ij_qmllang_align_multiline_binary_operation = false 526 + ij_qmllang_align_multiline_chained_methods = false 527 + ij_qmllang_align_multiline_extends_list = false 528 + ij_qmllang_align_multiline_for = true 529 + ij_qmllang_align_multiline_parameters = true 530 + ij_qmllang_align_multiline_parameters_in_calls = false 531 + ij_qmllang_align_multiline_ternary_operation = false 532 + ij_qmllang_align_object_properties = 0 533 + ij_qmllang_align_union_types = false 534 + ij_qmllang_align_var_statements = 0 535 + ij_qmllang_array_initializer_new_line_after_left_brace = false 536 + ij_qmllang_array_initializer_right_brace_on_new_line = false 537 + ij_qmllang_array_initializer_wrap = off 538 + ij_qmllang_assignment_wrap = off 539 + ij_qmllang_binary_operation_sign_on_next_line = false 540 + ij_qmllang_binary_operation_wrap = off 541 + ij_qmllang_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** 542 + ij_qmllang_blank_lines_after_imports = 1 543 + ij_qmllang_blank_lines_around_class = 1 544 + ij_qmllang_blank_lines_around_field = 0 545 + ij_qmllang_blank_lines_around_function = 1 546 + ij_qmllang_blank_lines_around_method = 1 547 + ij_qmllang_block_brace_style = end_of_line 548 + ij_qmllang_block_comment_add_space = false 549 + ij_qmllang_block_comment_at_first_column = true 550 + ij_qmllang_call_parameters_new_line_after_left_paren = false 551 + ij_qmllang_call_parameters_right_paren_on_new_line = false 552 + ij_qmllang_call_parameters_wrap = off 553 + ij_qmllang_catch_on_new_line = false 554 + ij_qmllang_chained_call_dot_on_new_line = true 555 + ij_qmllang_class_brace_style = end_of_line 556 + ij_qmllang_class_decorator_wrap = split_into_lines 557 + ij_qmllang_class_field_decorator_wrap = off 558 + ij_qmllang_class_method_decorator_wrap = off 559 + ij_qmllang_comma_on_new_line = false 560 + ij_qmllang_do_while_brace_force = never 561 + ij_qmllang_else_on_new_line = false 562 + ij_qmllang_enforce_trailing_comma = keep 563 + ij_qmllang_extends_keyword_wrap = off 564 + ij_qmllang_extends_list_wrap = off 565 + ij_qmllang_field_prefix = _ 566 + ij_qmllang_file_name_style = relaxed 567 + ij_qmllang_finally_on_new_line = false 568 + ij_qmllang_for_brace_force = never 569 + ij_qmllang_for_statement_new_line_after_left_paren = false 570 + ij_qmllang_for_statement_right_paren_on_new_line = false 571 + ij_qmllang_for_statement_wrap = off 572 + ij_qmllang_force_quote_style = false 573 + ij_qmllang_force_semicolon_style = false 574 + ij_qmllang_function_expression_brace_style = end_of_line 575 + ij_qmllang_function_parameter_decorator_wrap = off 576 + ij_qmllang_if_brace_force = never 577 + ij_qmllang_import_merge_members = global 578 + ij_qmllang_import_prefer_absolute_path = global 579 + ij_qmllang_import_sort_members = true 580 + ij_qmllang_import_sort_module_name = false 581 + ij_qmllang_import_use_node_resolution = true 582 + ij_qmllang_imports_wrap = on_every_item 583 + ij_qmllang_indent_case_from_switch = true 584 + ij_qmllang_indent_chained_calls = true 585 + ij_qmllang_indent_package_children = 0 586 + ij_qmllang_jsx_attribute_value = braces 587 + ij_qmllang_keep_blank_lines_in_code = 2 588 + ij_qmllang_keep_first_column_comment = true 589 + ij_qmllang_keep_indents_on_empty_lines = false 590 + ij_qmllang_keep_line_breaks = true 591 + ij_qmllang_keep_simple_blocks_in_one_line = false 592 + ij_qmllang_keep_simple_methods_in_one_line = false 593 + ij_qmllang_line_comment_add_space = true 594 + ij_qmllang_line_comment_at_first_column = false 595 + ij_qmllang_method_brace_style = end_of_line 596 + ij_qmllang_method_call_chain_wrap = off 597 + ij_qmllang_method_parameters_new_line_after_left_paren = false 598 + ij_qmllang_method_parameters_right_paren_on_new_line = false 599 + ij_qmllang_method_parameters_wrap = off 600 + ij_qmllang_object_literal_wrap = on_every_item 601 + ij_qmllang_object_types_wrap = on_every_item 602 + ij_qmllang_parentheses_expression_new_line_after_left_paren = false 603 + ij_qmllang_parentheses_expression_right_paren_on_new_line = false 604 + ij_qmllang_place_assignment_sign_on_next_line = false 605 + ij_qmllang_prefer_as_type_cast = false 606 + ij_qmllang_prefer_explicit_types_function_expression_returns = false 607 + ij_qmllang_prefer_explicit_types_function_returns = false 608 + ij_qmllang_prefer_explicit_types_vars_fields = false 609 + ij_qmllang_prefer_parameters_wrap = false 610 + ij_qmllang_property_prefix = 611 + ij_qmllang_reformat_c_style_comments = false 612 + ij_qmllang_space_after_colon = true 613 + ij_qmllang_space_after_comma = true 614 + ij_qmllang_space_after_dots_in_rest_parameter = false 615 + ij_qmllang_space_after_generator_mult = true 616 + ij_qmllang_space_after_property_colon = true 617 + ij_qmllang_space_after_quest = true 618 + ij_qmllang_space_after_type_colon = true 619 + ij_qmllang_space_after_unary_not = false 620 + ij_qmllang_space_before_async_arrow_lparen = true 621 + ij_qmllang_space_before_catch_keyword = true 622 + ij_qmllang_space_before_catch_left_brace = true 623 + ij_qmllang_space_before_catch_parentheses = true 624 + ij_qmllang_space_before_class_lbrace = true 625 + ij_qmllang_space_before_class_left_brace = true 626 + ij_qmllang_space_before_colon = true 627 + ij_qmllang_space_before_comma = false 628 + ij_qmllang_space_before_do_left_brace = true 629 + ij_qmllang_space_before_else_keyword = true 630 + ij_qmllang_space_before_else_left_brace = true 631 + ij_qmllang_space_before_finally_keyword = true 632 + ij_qmllang_space_before_finally_left_brace = true 633 + ij_qmllang_space_before_for_left_brace = true 634 + ij_qmllang_space_before_for_parentheses = true 635 + ij_qmllang_space_before_for_semicolon = false 636 + ij_qmllang_space_before_function_left_parenth = true 637 + ij_qmllang_space_before_generator_mult = false 638 + ij_qmllang_space_before_if_left_brace = true 639 + ij_qmllang_space_before_if_parentheses = true 640 + ij_qmllang_space_before_method_call_parentheses = false 641 + ij_qmllang_space_before_method_left_brace = true 642 + ij_qmllang_space_before_method_parentheses = false 643 + ij_qmllang_space_before_property_colon = false 644 + ij_qmllang_space_before_quest = true 645 + ij_qmllang_space_before_switch_left_brace = true 646 + ij_qmllang_space_before_switch_parentheses = true 647 + ij_qmllang_space_before_try_left_brace = true 648 + ij_qmllang_space_before_type_colon = false 649 + ij_qmllang_space_before_unary_not = false 650 + ij_qmllang_space_before_while_keyword = true 651 + ij_qmllang_space_before_while_left_brace = true 652 + ij_qmllang_space_before_while_parentheses = true 653 + ij_qmllang_spaces_around_additive_operators = true 654 + ij_qmllang_spaces_around_arrow_function_operator = true 655 + ij_qmllang_spaces_around_assignment_operators = true 656 + ij_qmllang_spaces_around_bitwise_operators = true 657 + ij_qmllang_spaces_around_equality_operators = true 658 + ij_qmllang_spaces_around_logical_operators = true 659 + ij_qmllang_spaces_around_multiplicative_operators = true 660 + ij_qmllang_spaces_around_relational_operators = true 661 + ij_qmllang_spaces_around_shift_operators = true 662 + ij_qmllang_spaces_around_unary_operator = false 663 + ij_qmllang_spaces_within_array_initializer_brackets = false 664 + ij_qmllang_spaces_within_brackets = false 665 + ij_qmllang_spaces_within_catch_parentheses = false 666 + ij_qmllang_spaces_within_for_parentheses = false 667 + ij_qmllang_spaces_within_if_parentheses = false 668 + ij_qmllang_spaces_within_imports = false 669 + ij_qmllang_spaces_within_interpolation_expressions = false 670 + ij_qmllang_spaces_within_method_call_parentheses = false 671 + ij_qmllang_spaces_within_method_parentheses = false 672 + ij_qmllang_spaces_within_object_literal_braces = false 673 + ij_qmllang_spaces_within_object_type_braces = true 674 + ij_qmllang_spaces_within_parentheses = false 675 + ij_qmllang_spaces_within_switch_parentheses = false 676 + ij_qmllang_spaces_within_type_assertion = false 677 + ij_qmllang_spaces_within_union_types = true 678 + ij_qmllang_spaces_within_while_parentheses = false 679 + ij_qmllang_special_else_if_treatment = true 680 + ij_qmllang_ternary_operation_signs_on_next_line = false 681 + ij_qmllang_ternary_operation_wrap = off 682 + ij_qmllang_union_types_wrap = on_every_item 683 + ij_qmllang_use_chained_calls_group_indents = false 684 + ij_qmllang_use_double_quotes = true 685 + ij_qmllang_use_explicit_js_extension = auto 686 + ij_qmllang_use_import_type = auto 687 + ij_qmllang_use_path_mapping = always 688 + ij_qmllang_use_public_modifier = false 689 + ij_qmllang_use_semicolon_after_statement = true 690 + ij_qmllang_var_declaration_wrap = normal 691 + ij_qmllang_while_brace_force = never 692 + ij_qmllang_while_on_new_line = false 693 + ij_qmllang_wrap_comments = false 694 + 695 + [{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock,uv.lock}] 696 + ij_toml_keep_indents_on_empty_lines = false
+4
.envrc
··· 1 + if nix flake show &>/dev/null; then 2 + use flake 3 + fi 4 + export DATABASE_URL="sqlite3://$(pwd)/data/instance.db"
+7
.gitattributes
··· 1 + 2 + * text=auto 3 + *.svg linguist-detectable 4 + *_ffi.ts linguist-language=Gleam 5 + *_ffi.mjs linguist-language=Gleam 6 + /notes/**/* linguist-documentation=true 7 + /notes/.obsidian/**/* linguist-generated=true
+37
.gitignore
··· 1 + *.beam 2 + *.ez 3 + /target/ 4 + erl_crash.dump 5 + /test 6 + *.log 7 + package-lock.json 8 + node_modules 9 + # Ignore Editor files 10 + .idea/ 11 + .vscode/ 12 + 13 + 14 + 15 + # Added by cargo 16 + /target 17 + 18 + # Added by me 19 + client/build 20 + /server/build 21 + /server/priv/static/lumina_client*.css 22 + /server/priv/static/lumina_client*.hash 23 + /server/priv/static/lumina_client*.mjs 24 + .env 25 + /data 26 + /built 27 + # mise system-specific config file 28 + .mise.toml 29 + 30 + 31 + # Obsidian 32 + /notes/.obsidian/appearance.json 33 + /notes/.obsidian/workspace.json 34 + /notes/.obsidian/app.json 35 + 36 + # Direnv cache 37 + .direnv/
+8
.idea/.gitignore
··· 1 + # Default ignored files 2 + /shelf/ 3 + /workspace.xml 4 + # Editor-based HTTP Client requests 5 + /httpRequests/ 6 + # Datasource local storage ignored files 7 + /dataSources/ 8 + /dataSources.local.xml
+12
.idea/Lumina.iml
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <module type="EMPTY_MODULE" version="4"> 3 + <component name="NewModuleRootManager"> 4 + <content url="file://$MODULE_DIR$"> 5 + <sourceFolder url="file://$MODULE_DIR$/server/src" isTestSource="false" /> 6 + <excludeFolder url="file://$MODULE_DIR$/data" /> 7 + <excludeFolder url="file://$MODULE_DIR$/target" /> 8 + </content> 9 + <orderEntry type="inheritedJdk" /> 10 + <orderEntry type="sourceFolder" forTests="false" /> 11 + </component> 12 + </module>
+6
.idea/copyright/GNU_AGPLv3.xml
··· 1 + <component name="CopyrightManager"> 2 + <copyright> 3 + <option name="notice" value=" Lumina/Peonies&#10; Copyright (C) 2018-2026 MLC 'Strawmelonjuice' Bloeiman and contributors.&#10;&#10; This program is free software: you can redistribute it and/or modify&#10; it under the terms of the GNU Affero General Public License as published&#10; by the Free Software Foundation, either version 3 of the License, or&#10; (at your option) any later version.&#10;&#10; This program is distributed in the hope that it will be useful,&#10; but WITHOUT ANY WARRANTY; without even the implied warranty of&#10; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the&#10; GNU Affero General Public License for more details.&#10;&#10; You should have received a copy of the GNU Affero General Public License&#10; along with this program. If not, see &lt;https://www.gnu.org/licenses/&gt;." /> 4 + <option name="myName" value="GNU AGPLv3" /> 5 + </copyright> 6 + </component>
+7
.idea/copyright/profiles_settings.xml
··· 1 + <component name="CopyrightManager"> 2 + <settings default="GNU AGPLv3"> 3 + <module2copyright> 4 + <element module="Source files" copyright="GNU AGPLv3" /> 5 + </module2copyright> 6 + </settings> 7 + </component>
+16
.idea/inspectionProfiles/Project_Default.xml
··· 1 + <component name="InspectionProjectProfileManager"> 2 + <profile version="1.0"> 3 + <option name="myName" value="Project Default" /> 4 + <inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true"> 5 + <Languages> 6 + <language minSize="58" name="Rust" /> 7 + </Languages> 8 + </inspection_tool> 9 + <inspection_tool class="RsUnusedImport" enabled="false" level="WARNING" enabled_by_default="false" /> 10 + <inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false"> 11 + <option name="processCode" value="true" /> 12 + <option name="processLiterals" value="true" /> 13 + <option name="processComments" value="true" /> 14 + </inspection_tool> 15 + </profile> 16 + </component>
+8
.idea/modules.xml
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <project version="4"> 3 + <component name="ProjectModuleManager"> 4 + <modules> 5 + <module fileurl="file://$PROJECT_DIR$/.idea/Lumina.iml" filepath="$PROJECT_DIR$/.idea/Lumina.iml" /> 6 + </modules> 7 + </component> 8 + </project>
+3
.idea/scopes/Source_files.xml
··· 1 + <component name="DependencyValidationManager"> 2 + <scope name="Source files" pattern="(file:*.rs||file:*.gleam||file:*.svg||file:*.css)&amp;&amp;!file[Lumina]:notes//*" /> 3 + </component>
+6
.idea/vcs.xml
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <project version="4"> 3 + <component name="VcsDirectoryMappings"> 4 + <mapping directory="" vcs="Git" /> 5 + </component> 6 + </project>
+29
.tangled/workflows/check.yaml
··· 1 + when: 2 + - event: ["push", "manual"] 3 + branch: ["develop"] 4 + - event: ["pull_request"] 5 + branch: ["main"] # We have no main, yet. 6 + 7 + engine: "nixery" 8 + 9 + # using the default values 10 + clone: 11 + skip: false 12 + depth: 1 13 + submodules: false 14 + 15 + dependencies: 16 + nixpkgs/nixpkgs-unstable: 17 + # I wish I could just use the flake, but alas aargh 18 + - mise 19 + 20 + #environment: 21 + # MY_ENV_VAR: "MY_ENV_VALUE" 22 + 23 + steps: 24 + - name: "Run tests through Mise" 25 + # I don't think this'll work, we have no database to reflect from! 26 + command: "mise i && mise run check" 27 + environment: 28 + GOOS: "darwin" 29 + GOARCH: "arm64"
+5
.tool-versions
··· 1 + bun 1.2.20 2 + gleam 1.15.0 3 + just latest 4 + watchexec latest 5 + dbmate latest
+329
LICENCE
··· 1 + Lumina/Peonies 2 + Copyright (C) 2018-2026 M.L.C. 'Strawmelonjuice' Bloeiman and contributors. 3 + 4 + =============================================================================== 5 + OFFICIAL NOTICE: RESERVATION OF RIGHTS & DERIVATIVE WORKS (ART. 4(3) DIR 2019/790) 6 + =============================================================================== 7 + 1. MACHINE-READABLE OPT-OUT: In accordance with Article 4(3) of Directive 8 + (EU) 2019/790, the Licensor hereby expressly reserves all rights for 9 + Text and Data Mining (TDM) and the training of Artificial Intelligence (AI) 10 + models in an appropriate and machine-readable manner. 11 + 12 + A robots.txt is added to the web interface of this program itself (/robots.txt), 13 + and exists as a file in the root of the code repository available on 14 + <https://tangled.org/strawmelonjuice.com/Lumina> and official mirrors. 15 + 16 + 17 + 2. CLARIFICATION ON DERIVATION: For the purpose of Article 1 of this Licence, 18 + the Licensor defines the use of this Work for training, fine-tuning, or 19 + optimizing AI models as the creation of a Derivative Work. 20 + 21 + 3. COPYLEFT OBLIGATION: Any party utilizing this Work for the development 22 + of AI models acknowledges that the resulting model weights, parameters, 23 + and architecture are subject to the Copyleft Clause (Article 5) of the 24 + EUPL v1.2. Distribution of such derivatives must occur under the terms 25 + of the EUPL or a Compatible Licence. 26 + 27 + 4. SEVERABILITY: Pursuant to Article 13, if any provision of this Special 28 + Notice is found to be invalid or unenforceable, the validity of the 29 + remainder of the Licence and its core Copyleft protections shall remain 30 + in full effect. 31 + =============================================================================== 32 + 33 + For the EUPL v1.2 in other languages, see 34 + <https://interoperable-europe.ec.europa.eu/collection/eupl/eupl-text-eupl-12>. 35 + 36 + This software is licensed under the European Union Public Licence (EUPL) v1.2. 37 + 38 + EUROPEAN UNION PUBLIC LICENCE v. 1.2 39 + EUPL © the European Union 2007, 2016 40 + 41 + This European Union Public Licence (the ‘EUPL’) applies to the Work (as 42 + defined below) which is provided under the terms of this Licence. Any use of 43 + the Work, other than as authorised under this Licence is prohibited (to the 44 + extent such use is covered by a right of the copyright holder of the Work). 45 + 46 + The Work is provided under the terms of this Licence when the Licensor (as 47 + defined below) has placed the following notice immediately following the 48 + copyright notice for the Work: 49 + 50 + Licensed under the EUPL 51 + 52 + or has expressed by any other means his willingness to license under the EUPL. 53 + 54 + 1. Definitions 55 + 56 + In this Licence, the following terms have the following meaning: 57 + 58 + - ‘The Licence’: this Licence. 59 + 60 + - ‘The Original Work’: the work or software distributed or communicated by the 61 + Licensor under this Licence, available as Source Code and also as Executable 62 + Code as the case may be. 63 + 64 + - ‘Derivative Works’: the works or software that could be created by the 65 + Licensee, based upon the Original Work or modifications thereof. This 66 + Licence does not define the extent of modification or dependence on the 67 + Original Work required in order to classify a work as a Derivative Work; 68 + this extent is determined by copyright law applicable in the country 69 + mentioned in Article 15. 70 + 71 + - ‘The Work’: the Original Work or its Derivative Works. 72 + 73 + - ‘The Source Code’: the human-readable form of the Work which is the most 74 + convenient for people to study and modify. 75 + 76 + - ‘The Executable Code’: any code which has generally been compiled and which 77 + is meant to be interpreted by a computer as a program. 78 + 79 + - ‘The Licensor’: the natural or legal person that distributes or communicates 80 + the Work under the Licence. 81 + 82 + - ‘Contributor(s)’: any natural or legal person who modifies the Work under 83 + the Licence, or otherwise contributes to the creation of a Derivative Work. 84 + 85 + - ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of 86 + the Work under the terms of the Licence. 87 + 88 + - ‘Distribution’ or ‘Communication’: any act of selling, giving, lending, 89 + renting, distributing, communicating, transmitting, or otherwise making 90 + available, online or offline, copies of the Work or providing access to its 91 + essential functionalities at the disposal of any other natural or legal 92 + person. 93 + 94 + 2. Scope of the rights granted by the Licence 95 + 96 + The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, 97 + sublicensable licence to do the following, for the duration of copyright 98 + vested in the Original Work: 99 + 100 + - use the Work in any circumstance and for all usage, 101 + - reproduce the Work, 102 + - modify the Work, and make Derivative Works based upon the Work, 103 + - communicate to the public, including the right to make available or display 104 + the Work or copies thereof to the public and perform publicly, as the case 105 + may be, the Work, 106 + - distribute the Work or copies thereof, 107 + - lend and rent the Work or copies thereof, 108 + - sublicense rights in the Work or copies thereof. 109 + 110 + Those rights can be exercised on any media, supports and formats, whether now 111 + known or later invented, as far as the applicable law permits so. 112 + 113 + In the countries where moral rights apply, the Licensor waives his right to 114 + exercise his moral right to the extent allowed by law in order to make 115 + effective the licence of the economic rights here above listed. 116 + 117 + The Licensor grants to the Licensee royalty-free, non-exclusive usage rights 118 + to any patents held by the Licensor, to the extent necessary to make use of 119 + the rights granted on the Work under this Licence. 120 + 121 + 3. Communication of the Source Code 122 + 123 + The Licensor may provide the Work either in its Source Code form, or as 124 + Executable Code. If the Work is provided as Executable Code, the Licensor 125 + provides in addition a machine-readable copy of the Source Code of the Work 126 + along with each copy of the Work that the Licensor distributes or indicates, 127 + in a notice following the copyright notice attached to the Work, a repository 128 + where the Source Code is easily and freely accessible for as long as the 129 + Licensor continues to distribute or communicate the Work. 130 + 131 + 4. Limitations on copyright 132 + 133 + Nothing in this Licence is intended to deprive the Licensee of the benefits 134 + from any exception or limitation to the exclusive rights of the rights owners 135 + in the Work, of the exhaustion of those rights or of other applicable 136 + limitations thereto. 137 + 138 + 5. Obligations of the Licensee 139 + 140 + The grant of the rights mentioned above is subject to some restrictions and 141 + obligations imposed on the Licensee. Those obligations are the following: 142 + 143 + Attribution right: The Licensee shall keep intact all copyright, patent or 144 + trademarks notices and all notices that refer to the Licence and to the 145 + disclaimer of warranties. The Licensee must include a copy of such notices and 146 + a copy of the Licence with every copy of the Work he/she distributes or 147 + communicates. The Licensee must cause any Derivative Work to carry prominent 148 + notices stating that the Work has been modified and the date of modification. 149 + 150 + Copyleft clause: If the Licensee distributes or communicates copies of the 151 + Original Works or Derivative Works, this Distribution or Communication will be 152 + done under the terms of this Licence or of a later version of this Licence 153 + unless the Original Work is expressly distributed only under this version of 154 + the Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee 155 + (becoming Licensor) cannot offer or impose any additional terms or conditions 156 + on the Work or Derivative Work that alter or restrict the terms of the 157 + Licence. 158 + 159 + Compatibility clause: If the Licensee Distributes or Communicates Derivative 160 + Works or copies thereof based upon both the Work and another work licensed 161 + under a Compatible Licence, this Distribution or Communication can be done 162 + under the terms of this Compatible Licence. For the sake of this clause, 163 + ‘Compatible Licence’ refers to the licences listed in the appendix attached to 164 + this Licence. Should the Licensee's obligations under the Compatible Licence 165 + conflict with his/her obligations under this Licence, the obligations of the 166 + Compatible Licence shall prevail. 167 + 168 + Provision of Source Code: When distributing or communicating copies of the 169 + Work, the Licensee will provide a machine-readable copy of the Source Code or 170 + indicate a repository where this Source will be easily and freely available 171 + for as long as the Licensee continues to distribute or communicate the Work. 172 + 173 + Legal Protection: This Licence does not grant permission to use the trade 174 + names, trademarks, service marks, or names of the Licensor, except as required 175 + for reasonable and customary use in describing the origin of the Work and 176 + reproducing the content of the copyright notice. 177 + 178 + 6. Chain of Authorship 179 + 180 + The original Licensor warrants that the copyright in the Original Work granted 181 + hereunder is owned by him/her or licensed to him/her and that he/she has the 182 + power and authority to grant the Licence. 183 + 184 + Each Contributor warrants that the copyright in the modifications he/she 185 + brings to the Work are owned by him/her or licensed to him/her and that he/she 186 + has the power and authority to grant the Licence. 187 + 188 + Each time You accept the Licence, the original Licensor and subsequent 189 + Contributors grant You a licence to their contributions to the Work, under the 190 + terms of this Licence. 191 + 192 + 7. Disclaimer of Warranty 193 + 194 + The Work is a work in progress, which is continuously improved by numerous 195 + Contributors. It is not a finished work and may therefore contain defects or 196 + ‘bugs’ inherent to this type of development. 197 + 198 + For the above reason, the Work is provided under the Licence on an ‘as is’ 199 + basis and without warranties of any kind concerning the Work, including 200 + without limitation merchantability, fitness for a particular purpose, absence 201 + of defects or errors, accuracy, non-infringement of intellectual property 202 + rights other than copyright as stated in Article 6 of this Licence. 203 + 204 + This disclaimer of warranty is an essential part of the Licence and a 205 + condition for the grant of any rights to the Work. 206 + 207 + 8. Disclaimer of Liability 208 + 209 + Except in the cases of wilful misconduct or damages directly caused to natural 210 + persons, the Licensor will in no event be liable for any direct or indirect, 211 + material or moral, damages of any kind, arising out of the Licence or of the 212 + use of the Work, including without limitation, damages for loss of goodwill, 213 + work stoppage, computer failure or malfunction, loss of data or any commercial 214 + damage, even if the Licensor has been advised of the possibility of such 215 + damage. However, the Licensor will be liable under statutory product liability 216 + laws as far such laws apply to the Work. 217 + 218 + 9. Additional agreements 219 + 220 + While distributing the Work, You may choose to conclude an additional 221 + agreement, defining obligations or services consistent with this Licence. 222 + However, if accepting obligations, You may act only on your own behalf and on 223 + your sole responsibility, not on behalf of the original Licensor or any other 224 + Contributor, and only if You agree to indemnify, defend, and hold each 225 + Contributor harmless for any liability incurred by, or claims asserted against 226 + such Contributor by the fact You have accepted any warranty or additional 227 + liability. 228 + 229 + 10. Acceptance of the Licence 230 + 231 + The provisions of this Licence can be accepted by clicking on an icon ‘I 232 + agree’ placed under the bottom of a window displaying the text of this Licence 233 + or by affirming consent in any other similar way, in accordance with the rules 234 + of applicable law. Clicking on that icon indicates your clear and irrevocable 235 + acceptance of this Licence and all of its terms and conditions. 236 + 237 + Similarly, you irrevocably accept this Licence and all of its terms and 238 + conditions by exercising any rights granted to You by Article 2 of this 239 + Licence, such as the use of the Work, the creation by You of a Derivative Work 240 + or the Distribution or Communication by You of the Work or copies thereof. 241 + 242 + 11. Information to the public 243 + 244 + In case of any Distribution or Communication of the Work by means of 245 + electronic communication by You (for example, by offering to download the Work 246 + from a remote location) the distribution channel or media (for example, a 247 + website) must at least provide to the public the information requested by the 248 + applicable law regarding the Licensor, the Licence and the way it may be 249 + accessible, concluded, stored and reproduced by the Licensee. 250 + 251 + 12. Termination of the Licence 252 + 253 + The Licence and the rights granted hereunder will terminate automatically upon 254 + any breach by the Licensee of the terms of the Licence. 255 + 256 + Such a termination will not terminate the licences of any person who has 257 + received the Work from the Licensee under the Licence, provided such persons 258 + remain in full compliance with the Licence. 259 + 260 + 13. Miscellaneous 261 + 262 + Without prejudice of Article 9 above, the Licence represents the complete 263 + agreement between the Parties as to the Work. 264 + 265 + If any provision of the Licence is invalid or unenforceable under applicable 266 + law, this will not affect the validity or enforceability of the Licence as a 267 + whole. Such provision will be construed or reformed so as necessary to make it 268 + valid and enforceable. 269 + 270 + The European Commission may publish other linguistic versions or new versions 271 + of this Licence or updated versions of the Appendix, so far this is required 272 + and reasonable, without reducing the scope of the rights granted by the 273 + Licence. New versions of the Licence will be published with a unique version 274 + number. 275 + 276 + All linguistic versions of this Licence, approved by the European Commission, 277 + have identical value. Parties can take advantage of the linguistic version of 278 + their choice. 279 + 280 + 14. Jurisdiction 281 + 282 + Without prejudice to specific agreement between parties, 283 + 284 + - any litigation resulting from the interpretation of this License, arising 285 + between the European Union institutions, bodies, offices or agencies, as a 286 + Licensor, and any Licensee, will be subject to the jurisdiction of the Court 287 + of Justice of the European Union, as laid down in article 272 of the Treaty 288 + on the Functioning of the European Union, 289 + 290 + - any litigation arising between other parties and resulting from the 291 + interpretation of this License, will be subject to the exclusive 292 + jurisdiction of the competent court where the Licensor resides or conducts 293 + its primary business. 294 + 295 + 15. Applicable Law 296 + 297 + Without prejudice to specific agreement between parties, 298 + 299 + - this Licence shall be governed by the law of the European Union Member State 300 + where the Licensor has his seat, resides or has his registered office, 301 + 302 + - this licence shall be governed by Belgian law if the Licensor has no seat, 303 + residence or registered office inside a European Union Member State. 304 + 305 + Appendix 306 + 307 + ‘Compatible Licences’ according to Article 5 EUPL are: 308 + 309 + - GNU General Public License (GPL) v. 2, v. 3 310 + - GNU Affero General Public License (AGPL) v. 3 311 + - Open Software License (OSL) v. 2.1, v. 3.0 312 + - Eclipse Public License (EPL) v. 1.0 313 + - CeCILL v. 2.0, v. 2.1 314 + - Mozilla Public Licence (MPL) v. 2 315 + - GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 316 + - Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for 317 + works other than software 318 + - European Union Public Licence (EUPL) v. 1.1, v. 1.2 319 + - Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong 320 + Reciprocity (LiLiQ-R+). 321 + 322 + The European Commission may update this Appendix to later versions of the 323 + above licences without producing a new version of the EUPL, as long as they 324 + provide the rights granted in Article 2 of this Licence and protect the 325 + covered Source Code from exclusive appropriation. 326 + 327 + All other changes or additions to this Appendix require the production of a 328 + new EUPL version. 329 +
+11
README.md
··· 1 + # Lumina(/peonies) server 2 + 3 + > Notice: 4 + > This project lives on [Tangled](https://tangled.org/strawmelonjuice.com/Lumina), it is mirrored to Codeberg and a few 5 + > other places, but the main development happens on Tangled. 6 + > Please report issues and contribute on Tangled. 7 + 8 + Lumina is a project in development, as the short description says "Just trying out an old concept.". It is not in any 9 + way ready for you to try. However, you are encouraged to contribute in any way! 10 + 11 + Currently, as no stable is produced yet, code lives mainly in the `development` branch.
+8
metadata.json
··· 1 + { 2 + "version": "1.0", 3 + "attribution": "MLC Strawmelonjuice Bloeiman", 4 + "tdm_reservation": true, 5 + "ai_training_allowed": false, 6 + "license": "EUPL-1.2", 7 + "derivative_work_claim": "AI models trained on this source are considered derivative works." 8 + }
+3
notes/.obsidian/backlink.json
··· 1 + { 2 + "backlinkInDocument": true 3 + }
+1
notes/.obsidian/community-plugins.json
··· 1 + []
+33
notes/.obsidian/core-plugins.json
··· 1 + { 2 + "file-explorer": true, 3 + "global-search": true, 4 + "switcher": true, 5 + "graph": true, 6 + "backlink": true, 7 + "canvas": true, 8 + "outgoing-link": true, 9 + "tag-pane": true, 10 + "footnotes": true, 11 + "properties": false, 12 + "page-preview": true, 13 + "daily-notes": true, 14 + "templates": true, 15 + "note-composer": true, 16 + "command-palette": true, 17 + "slash-command": false, 18 + "editor-status": true, 19 + "bookmarks": true, 20 + "markdown-importer": false, 21 + "zk-prefixer": true, 22 + "random-note": false, 23 + "outline": true, 24 + "word-count": true, 25 + "slides": false, 26 + "audio-recorder": false, 27 + "workspaces": false, 28 + "file-recovery": true, 29 + "publish": false, 30 + "sync": false, 31 + "bases": true, 32 + "webviewer": false 33 + }
+22
notes/.obsidian/graph.json
··· 1 + { 2 + "collapse-filter": true, 3 + "search": "", 4 + "showTags": false, 5 + "showAttachments": false, 6 + "hideUnresolved": false, 7 + "showOrphans": true, 8 + "collapse-color-groups": true, 9 + "colorGroups": [], 10 + "collapse-display": true, 11 + "showArrow": false, 12 + "textFadeMultiplier": 0, 13 + "nodeSizeMultiplier": 1, 14 + "lineSizeMultiplier": 1, 15 + "collapse-forces": true, 16 + "centerStrength": 0.518713248970312, 17 + "repelStrength": 10, 18 + "linkStrength": 1, 19 + "linkDistance": 250, 20 + "scale": 1, 21 + "close": true 22 + }
+14
notes/Design choices/Future concepts/Federation/IIC (Interinstance Communication).md
··· 1 + The server should a poll-inspired syncing system for Federating posts with other servers (instances). This is a [[High-level requirements#^4c6bf0|must]]. 2 + 3 + Having established this, a _how_ remains. In an earlier iteration of Lumina/Ephew (`Lumina:Peonies:itr1`), this how was conceptually answered by introducing HTTP requests fetching other instances' post IDs and then letting the client fetch the actual post content. This is a sound strategy in theory, however, you would possibly fetch posts from an instance impersonating the instance you talked to previously. 4 + To keep this from happening, a choice was made to only accept domain names as instance ID's, this choice is still present in the current iteration, however, DNS is not infallible. 5 + 6 + To solve that, some form of key checking has to be done. Either by sharing a secret token and having an instance store it, or... more sane, by `ed25519`? 7 + 8 + Furthermore, these relatively big HTTP requests would be rate-limited on the requesting side, on the serving side, request spammers would be autoremoved from the allowlist. 9 + 10 + That initially created the concept of WebSocket connections, preferably ones that stay open forever (which is a long time). However, more recently, the [polyproto](https://polyproto.org) has been on my mind. 11 + 12 + ### Polyproto 13 + 14 + I have to look into protocol-specific details later, however, each Lumina instance could also be a Polyproto 'homeserver', thereby allowing the instance to communicate to other instances using an instance user e.g., `iic@peonies.xyz`, AND as users on the instance, e.g. `user+comment@peonies.xyz`. Then federating timelines would of course go over the iic username, however things like comments or DM's, that'd require more direct federation, would be sent directly using JSON from `<username>+<reason>@<instance>` to the instance user of another instance. One of the pitfalls of the earlier conceptual implementation, was that due to the rate-limits of HTTP polling, there was at least 30 seconds of delay, this concept seems to resolve that fantastically.
+11
notes/Design choices/Future concepts/Push notifications/push notification resolve links.md
··· 1 + I notice on other social media platforms that when an edit or upload has unforseen consequences, this usually results in 2 + broken push notifications. 3 + Notifications that lead to a 404. 4 + 5 + When creating a push notification for something we know nothing about, this is what it is. But in Lumina these 6 + notifications lead to a postview using a JSON string after the # in the url (url hash). 7 + 8 + That JSON string can just contain a post or notification id, but we generate a preview of a post, and that 9 + preview should be pushed but not saved once again in the database. Instead, we add to the JSON object. Client holds an 10 + absolute reference to the post id, but also requests a post by both id and a part of its preview. Lumina client should 11 + be smart enough to figure out what post this is even when the ID leads to a 404.
+69
notes/Design choices/Philosophies/'Timeline carries most' and the database.md
··· 1 + Rationale: [[Backend > Timeline carries most]] 2 + # Lumina Data Storage Architecture 3 + 4 + This document outlines the data storage architecture of the Lumina social platform, based on the existing implementation 5 + and design principles. 6 + 7 + 8 + 9 + ## Core Philosophy: "Timeline-carries-most" 10 + 11 + Lumina's storage is designed around a central principle referred to as "Timeline-carries-most". The core idea is that 12 + the `timelines` table, which is expected to be the most frequently accessed table, should be as minimal and efficient as 13 + possible. 14 + 15 + - It acts as an index, containing only a timeline ID (`tlid`), an item ID (`item_id`), and a `timestamp`. 16 + - It does not store any content itself, only the relationship between a timeline and an item. 17 + 18 + 19 + ## Database System 20 + 21 + Lumina supports two SQL database backends: 22 + 23 + - **PostgreSQL**: The recommended database for production environments. 24 + - **SQLite**: Supported for testing and development purposes. 25 + 26 + The choice of a database is configured via the `LUMINA_DB_TYPE` environment variable. 27 + 28 + ## Item and Content Storage 29 + 30 + Content in Lumina is stored in a flexible, multi-table system that allows for various types of items to be added to 31 + timelines. 32 + 33 + ### 1. The Item Lookup Table (`itemtypelookupdb`) 34 + 35 + This table acts as a central directory or "forwarding table". Its purpose is to map a generic `item_id` to its specific 36 + content type. 37 + 38 + - It contains an `item_id` and an `itemtype` string. 39 + - The `itemtype` string directly corresponds to the name of the database table where the item's specific data is 40 + stored (e.g., `post_text`, `post_article`). 41 + 42 + ### 2. Specific Content Tables 43 + 44 + Each type of content has its own dedicated table. This design allows for adding new content types without altering the 45 + core timeline logic. The initial three content tables are: 46 + 47 + - `post_text`: For short, microblog-style text posts. 48 + - `post_media`: For media-focused posts (e.g., images, videos). The table stores a reference to a **MinIO object ID**, 49 + not the media file itself. 50 + - `post_article`: For long-form content with a title and body. 51 + 52 + For direct messages among others, there will be more variants of these. 53 + 54 + ### 3. Handling Foreign Content (Federation) 55 + 56 + The content tables are designed to accommodate posts from other federated instances. Each content table includes the 57 + following nullable fields: 58 + 59 + - `foreign_instance_id`: Stores the identifier of the instance where the post originated. 60 + - `foreign_post_id`: Stores the post's original ID from its home instance. 61 + 62 + This allows a local copy of the content to exist while preserving a reference to its original source. 63 + 64 + ## Caching Layer 65 + 66 + - **Redis** is used as a caching and performance-optimization layer. 67 + - It is used for timeline caching and for ephemeral data structures like Bloom filters to quickly check for the 68 + existence of usernames and emails. 69 + - Redis does not store any persistent, canonical data.
+1
notes/Design choices/Philosophies/Frontend > Non-minimalist design.md
··· 1 + Lumina is not minimalistic. It follows web design trends from decades ago with that approach, but also prefers a clean and modern design. This is why coloured elements are very important, but it also allows us to implement features considered old-school nowadays. Like the ability
+57
notes/Design choices/Rationale/Backend > Connection pooling and caching.md
··· 1 + ## Overview 2 + - PostgreSQL and Redis are pooled with `bb8` (see `server/src/database.rs`). 3 + - Redis is used only for performance: bloom filters and timeline caches; PostgreSQL remains source of truth. 4 + - Background maintainer (`database::maintain`) periodically deletes old sessions and prunes timeline caches. 5 + 6 + ## PostgreSQL pool (bb8-postgres) 7 + - Built from `tokio_postgres::Config` with `NoTls`. 8 + - Pools are cloned everywhere via `DatabaseConnections::get_postgres_pool` to keep acquisition cheap. 9 + - Avoid `unwrap()` on `.get()`; surface bb8 run errors via `LuminaError::Bb8RunErrorPg`. 10 + 11 + Example (simplified): 12 + ```rust 13 + let pg_pool = PgConn { postgres_pool, redis_pool }; 14 + let client = pg_pool.postgres_pool.get().await?; // ? maps to LuminaError::Bb8RunErrorPg 15 + ``` 16 + 17 + ## Redis pool (bb8-redis) 18 + - Configured pool builder (currently max_size 50, 5s timeout, 5m idle timeout). 19 + - Connection type is `MultiplexedConnection`; use `redis::cmd(...).query_async(&mut **conn)`. Pool errors surface as `LuminaError::Bb8RunErrorRedis`. 20 + 21 + ### Bloom filters 22 + - Keys: `bloom:email`, `bloom:username`. 23 + - Populated at startup from Postgres (`database::setup`). 24 + - Checked in `user::register_validitycheck` before DB uniqueness queries. 25 + 26 + Example add/check: 27 + ```rust 28 + let mut conn = redis_pool.get().await?; 29 + redis::cmd("BF.ADD").arg("bloom:email").arg(email).query_async(&mut *conn).await?; 30 + let exists: bool = redis::cmd("BF.EXISTS").arg("bloom:email").arg(email).query_async(&mut *conn).await?; 31 + ``` 32 + 33 + ### Timeline cache 34 + - Cache keys: `timeline_cache:{tlid}:page:{page}`; metadata key: `timeline_cache:{tlid}:meta`. 35 + - Cache TTL: 3600s; high-traffic threshold: 100 lookups (global always high-traffic). 36 + - Write path (`cache_timeline_page`) stores page JSON and total count; read path (`get_cached_timeline_page`) returns `CachedTimelinePage`. 37 + - Invalidation: `invalidate_timeline_cache` SCANs matching keys and DELs; called after timeline writes and from the maintainer loop when timelines change. 38 + - Background invalidation cursor uses `timeline_cache_last_check` stored in Redis. 39 + 40 + Example invalidate: 41 + ```rust 42 + let mut conn = redis_pool.get().await?; 43 + timeline::invalidate_timeline_cache(&mut conn, tlid).await?; 44 + ``` 45 + 46 + ## Background maintainer 47 + - `database::maintain` (spawned at setup) runs two intervals: 48 + - Every 60s: delete sessions older than 20 days. 49 + - Every 300s: prune expired timeline cache entries; check timeline invalidations based on latest timestamps. 50 + 51 + ## Tests 52 + - `src/tests.rs` covers: pool setup, bloom filter add/exists, timeline cache invalidation. 53 + 54 + ## Operational cautions 55 + - Pool exhaustion: handle `.get()` errors; avoid panics from `unwrap()`. 56 + - Redis is non-authoritative; always fall back to Postgres on cache miss or bloom filter hit. 57 + - Keep TTLs and thresholds in sync if tuning (`timeline.rs` constants).
+20
notes/Design choices/Rationale/Backend > Error handling and logging.md
··· 1 + ## LuminaError 2 + - Defined in `server/src/errors.rs`. 3 + - Key variants: `DbError(LuminaDbError)`, `Bb8RunErrorPg(bb8::RunError<postgres::Error>)`, `Bb8RunErrorRedis(bb8::RunError<redis::RedisError>)`, auth/registration errors, `SerializationError(String)`, `RocketFaillure(Box<rocket::Error>)`. 4 + - Conversions implemented for Rocket, Postgres, Redis, and bb8 run errors. 5 + - Guidance: propagate the source error (`?`) to keep context; avoid lossy `to_string()` unless necessary. 6 + 7 + ## Logging 8 + - Event logging macros `info_elog!`, `warn_elog!`, `error_elog!`, `success_elog!` are used across DB/timeline flows. 9 + - `EventLogger` can log to stdout and (optionally) Postgres `logs` table (see `helpers/events.rs`). 10 + - When logging DB failures, prefer structured context (timeline id, page, user) to aid diagnosis. 11 + 12 + ## Failure-handling patterns 13 + - Pool acquisition: use `?` so `.get()` maps to `LuminaError::Bb8RunErrorPg/Redis`; avoid panics. 14 + - Redis is non-authoritative: on Redis errors, proceed with Postgres path to avoid request failure where possible. 15 + - Bloom filters: treat `BF.EXISTS` positives as hints; always confirm with Postgres. 16 + - Timeline cache: cache misses/failures should fall back to DB fetch; invalidation is best-effort. 17 + 18 + ## Operational notes 19 + - If Rocket state is missing (e.g., limiter), guards fail-open; verify state wiring at startup. 20 + - Add tracing/metrics around pool usage and cache hit/miss for production readiness.
+27
notes/Design choices/Rationale/Backend > Rate limiting.md
··· 1 + ## Overview 2 + - Token-bucket limiter in `server/src/rate_limiter.rs` using in-memory `HashMap` protected by `tokio::sync::Mutex`. 3 + - Rocket request guard `RateLimit` pulls `State<GeneralRateLimiter>`; missing state = allow (fail-open). 4 + - Separate wrapper types: `GeneralRateLimiter` and `AuthRateLimiter` so Rocket can manage both independently. 5 + 6 + ## Defaults / Tuning 7 + - Constructor requires `refill_per_second` and `capacity`; no hardcoded defaults. Decide per endpoint. 8 + - In-memory only: resets on process restart; not distributed. For multi-node, replace with shared store (e.g., Redis token bucket) or IP hash partitioning. 9 + - Keyed by client IP (`Request::client_ip()`); missing IP maps to key "unknown". 10 + 11 + ## Usage pattern 12 + ```rust 13 + // Configure and mount in Rocket managed state 14 + let limiter = GeneralRateLimiter::new(refill_per_second, capacity); 15 + rocket::build().manage(limiter); 16 + 17 + // Handler signature adds guard 18 + #[get("/protected")] 19 + async fn protected(_rate: RateLimit) -> &'static str { 20 + "ok" 21 + } 22 + ``` 23 + 24 + ## Gotchas 25 + - Fail-open if the guard cannot fetch state; ensure the limiter is registered in Rocket. 26 + - No per-route tuning baked in; provide distinct limiters via type wrappers if needed. 27 + - Single-threaded bottleneck: Mutex over HashMap is fine for moderate QPS; consider sharding or lock-free structure if contention grows.
+25
notes/Design choices/Rationale/Backend > Timeline carries most.md
··· 1 + # Timeline-carries-most 2 + [['Timeline carries most' and the database]] 3 + 4 + One of the busiest tables you'll see is the timeline, containing just some ID's and timestamps. 5 + 6 + | Kind | timeline ID | item ID | Timestamp | 7 + | ---------------------------------------- | ----------- | ------- | ------------------ | 8 + | `'USER'`, `'DIRECT'`, `'TL'`, `'BUBBLE'` | uuidv4 | uuidv4 | Database timestamp | 9 + 10 + The `global` timeline, here being `00000000-0000-0000-0000-000000000000` as the only constant-assigned timeline ID. The 11 + user-profiles being the same as their user id counterpart. 12 + 13 + This is too vague to actually be able to pull a post, which is why the item forward table exists, combining a UUID and a 14 + string to forward to the right item. 15 + 16 + Now I say `item`, not `post` here. This because you might expect only timelines (global, userprofiles) and bubbles ( 17 + timelines meant for a specific subject, forming a community within the larger site) in this table, but direct message 18 + threads are actually also saved here. 19 + 20 + This means this table might become a little overcrowded, and optimizations such as caching, sharding and mirrorring to 21 + Redis will be needed to keep it somewhat performant, especially since this is essentially a constant hot path. I am 22 + aware. 23 + 24 + Which is why we also would need to log every timeline request to be able to identify for example over-requested 25 + timelines.
+6
notes/Design choices/Rationale/Web client > Gleam language.md
··· 1 + # The `gleam` language 2 + 3 + I am a big fan of the language. I think it's a great language for 4 + writing maintainable and scalable code. I think it's a great language 5 + for writing maintainable and scalable code. It's simplicity and the 6 + young yet strong community set a good foundation for Lumina.
+4
notes/High-level requirements.md
··· 1 + - Must provide a timeline for 'global' and user-specific (dynamic timelines), viewable on the front end. 2 + - Must be able to federate, and users be able to interact with "external" content ^4c6bf0 3 + - Must have a responsive web client implementing all 'Must' end-user-features. 4 + - Must have a web admin panel
+17
notes/README.md
··· 1 + This is `Lumina/Peonies`'s Obsidian vault for design choices, philosophies and concepts or even psuedocode. 2 + ## Earlier iterations 3 + 4 + `Lumina:Peonies:itr2` is the current and seemingly final iteration of this project, as of 2026. It uses a Rust server and Gleam/Lustre SPA as web frontend. 5 + 6 + This project has been conceptualised and prototyped into many earlier iterations before, each with different approaches and final result. Some known older iterations had different names, listing a few: 7 + 8 + | Codenamed | About | Introduced | 9 + | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | 10 + | _Peonies-Lumina_ | factually `Lumina:Peonies:itr1`, had a much bigger approach where multiple backends were explored, including ones based on the BEAM (Gleam-Erlang backend to be precise), is what itr2 draws most inspiration of.<br><br>Having multiple backends with non-matching features proved to be too complicated to maintain or draw straight. | Federation, conceptually | 11 + | _Lumina-Ephew_ | A concept-only iteration that never made it past the drawing board. | Lumina's principles and the global chronological timeline | 12 + | _Ephew_ | A near-complete PHP implementation with a plain HTML+CSS frontend (no scripts), fell apart due to the quickly aging PHP ecosystem at the time. | introducing the idea that 'multiple types of posts can feel native' | 13 + | FNew | A public text-only message pinboard | ~~Criticism, mostly~~ | 14 + 15 + 16 + 17 + The current iteration is a more well-documented and slower approach, giving time to learn and chances to refactor. It also comes in a time where the tech for it is perfect and
+1
notes/Todo's.md
··· 1 + Todo's from these notes are deprecated and replaced by Issues on the repository.
+21
robots.txt
··· 1 + # AI training and TDM are expressly reserved. 2 + # See LICENSE file for full legal terms. 3 + 4 + User-agent: * 5 + Disallow: / 6 + 7 + # Specifically targeting AI crawlers 8 + User-agent: GPTBot 9 + Disallow: / 10 + 11 + User-agent: ChatGPT-User 12 + Disallow: / 13 + 14 + User-agent: Google-Extended 15 + Disallow: / 16 + 17 + User-agent: CCBot 18 + Disallow: / 19 + 20 + X-Robots-Tag: noai 21 + X-Robots-Tag: noimageai