├── .github └── FUNDING.yml ├── .htaccess ├── 88x15-button1.jpg ├── 88x15-button2.jpg ├── LICENSE ├── README.md ├── cache └── readme.md ├── errors ├── 403.html └── 404.html ├── index.php └── posts ├── 2018 └── 09 │ └── 22 │ └── a-new-post.md ├── about └── main.md ├── error.css └── style.css /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [cypnk] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: cypnk 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: cypnk 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: cypnk 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | Options +FollowSymLinks 2 | RewriteEngine on 3 | 4 | # All requests to /data folder 5 | RewriteCond %{REQUEST_URI} ^/data [NC] 6 | 7 | # All requests to /cache folder 8 | RewriteCond %{REQUEST_URI} ^/cache [NC] 9 | 10 | # All requests to readme files 11 | RewriteCond %{REQUEST_URI} ^/readme [NC] 12 | RewriteRule ^.*$ /index.php [L,QSA] 13 | 14 | # All files and folders that don't exist 15 | RewriteCond %{REQUEST_FILENAME} !-f 16 | RewriteCond %{REQUEST_FILENAME} !-d 17 | 18 | # Send to /index.php (including any query strings added at the end) 19 | RewriteRule ^.*$ /index.php [L,QSA] 20 | -------------------------------------------------------------------------------- /88x15-button1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cypnk/Bare/96df7a06ac4725719e8fb61995cbb374560a6437/88x15-button1.jpg -------------------------------------------------------------------------------- /88x15-button2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cypnk/Bare/96df7a06ac4725719e8fb61995cbb374560a6437/88x15-button2.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2018, Rustic Cyberpunk 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bare 2 | A single file directory-to-blog 3 | 4 | Bare has no usernames, passwords, editors, file uploaders, or other mechanisms that get in the way of writing your thoughts. Simply put your posts in *year/month/day/slug.md* format, and Bare will publish it. 5 | 6 | The first line in each post is the title. The URL slug is the filename. Posts can be tagged by adding the last line in the following format: 7 | `tags: cabin, diy` 8 | 9 | You can also schedule posts to be published at a future date as bare will only show posts from the current date and into the past. Bare will create an archive of all posts and create a syndication feed of the most recent ones. 10 | 11 | An about page can be created by editing *posts/about/main.md*. Additional about pages can be added to the same directory and again the URL is the filename. About sub directories can also be added. The location of the "about" folder may be renamed to anything else URL friendly, and under 255 characters in length, in the "aboutview" URL route in *index.php*. 12 | 13 | A static homepage can be shown instead of the lastest posts by creating a *posts/home.md* page. Bare will show this similar to an about page. 14 | 15 | Posts are cached in a SQLite database created dynamically, which enables fast browsing and searching for posts by tags, title, or post body. The *cache.db* can be deleted at any time and Bare will rebuild it on the first visit to your blog. 16 | 17 | An extensive hook system allows customizing posts, indexes, and other template elements. Hooks also enable completely overriding the default behavior of most rendering functions and some core functions via [plugins](https://github.com/cypnk/Bare-Plugins). 18 | 19 | Bare's simple installation and minimal set of features make it ideal for hosting blogs on [Hidden Services](https://en.wikipedia.org/wiki/Tor_(anonymity_network)#Onion_services) on the Tor anonymity network. 20 | 21 | The author's [personal blog](https://rc.sh2.us) is using Bare, also mirrored on [Tor](http://kpz62k4pnyh5g5t2efecabkywt2aiwcnqylthqyywilqgxeiipen5xid.onion). Use [Tor Browser](https://www.torproject.org/) to visit the Tor mirror. 22 | 23 | ![Bare](https://raw.githubusercontent.com/cypnk/Bare/master/88x15-button1.jpg) 24 | ![Bare Powered](https://raw.githubusercontent.com/cypnk/Bare/master/88x15-button2.jpg) 25 | 26 | ## Table of contents 27 | * [Requirements](#requirements) 28 | * [Installation](#installation) 29 | * [Multiple blogs or shared content](#multiple-blogs-or-shared-content) 30 | * [Formatting](#content-formatting) 31 | * [Custom errors (optional)](#custom-errors-optional) 32 | * [Installing on other web servers](#installing-on-other-web-servers) 33 | * [Nginx](#nginx) 34 | * [OpenBSD's httpd(8) web server](#openbsds-httpd8-web-server) 35 | * [Running a TLS-enabled Bare blog on OpenBSD](#running-a-tls-enabled-bare-blog-on-openbsd) 36 | * [Troubleshooting](#troubleshooting) 37 | 38 | 39 | 40 | ## Requirements 41 | * Webserver capable of handling URL rewrites (Apache, Nginx etc...) 42 | * PHP Version 8+ (8.3+ recommended) 43 | 44 | The following PHP extensions may need to be installed or enabled in **php.ini**: 45 | * pdo_sqlite (*required*) 46 | * sqlite3 (*required*) 47 | * mbstring 48 | * fileinfo (*required*) 49 | * intl 50 | * tidy 51 | 52 | **Note:** Various Windows and Unix-like platforms have differing locations for [php.ini](https://www.php.net/manual/en/configuration.file.php). Check your installation or contact your administrator to gain access to this file. 53 | 54 | Remember to backup **php.ini** before making changes to it. 55 | 56 | The [GD extension](https://www.php.net/manual/en/intro.image.php) is suggested as plugins may use it, however it is not required for core functionality. If some plugins also need encryption, the [Sodium extension](https://www.php.net/manual/en/intro.sodium.php) is recommended. 57 | 58 | If you prefer to use [Composer](https://getcomposer.org/) to handle your environment (optional), use the following example **composer.json**: 59 | ``` 60 | { 61 | "require": { 62 | "php": ">=8.3", 63 | "lib-iconv": "*", 64 | "ext-pdo": "*", 65 | "ext-pdo_sqlite": "*", 66 | "ext-mbstring": "*", 67 | "ext-fileinfo": "*", 68 | "ext-intl" : "*", 69 | "ext-tidy" : "*" 70 | }, 71 | "suggest": { 72 | "ext-gd": "*", 73 | "ext-sodium": "*" 74 | }, 75 | "prefer-stable": true 76 | } 77 | ``` 78 | 79 | ## Installation 80 | **Note**: These instructions are for the [releases](https://github.com/cypnk/Bare/releases). Only use released Bare versions. 81 | 82 | Upload the following to your web root: 83 | * .htaccess - Required only if using the Apache web server 84 | * index.php - Your homepage 85 | * /posts folder - Contains your posts (use your favorite editor) and content such as CSS, media, or robots.txt 86 | * /cache folder - Formatted cache (needs write access) 87 | * /errors - Custom error pages (optional) 88 | 89 | The */posts*, */cache*, and */errors* folder locations can be adjusted in *index.php*. You may keep them in the same directory or outside the web root as long as PHP still has access to them. The */errors* folder can be excluded entirely if you don't intend to use any custom error pages. 90 | 91 | An optional **config.json** file can be created in the */cache* folder to override all but a few configuration defaults. The **config.json** uses typical [JSON formating](https://en.wikipedia.org/wiki/JSON) and will make it easier to upgrade Bare while preserving your settings. Creating a **config.json** is recommended for ease of upgrading to future releases of Bare. 92 | 93 | Bare will try to create a **startup.log** file in the */cache* folder if there are any missing requirements or missing optional libraries during the first visit to your blog. The **startup.log** should be deleted or renamed after installing or enabling the missing requirements before trying again. 94 | 95 | The following changes are to the settings in *index.php*, which also has the default theme so there are no other files to edit. 96 | 97 | Add your site's domain name, E.G. *example.com*, to SITES_ENABLED in **index.php** (currently, the author's Tor blog is in this place) or to the 'sites_enabled' setting in the **config.json** file. 98 | 99 | In **index.php**, the SITES_ENABLED setting may be as follows: 100 | ``` 101 | { 102 | "example.com" : [] 103 | } 104 | ``` 105 | 106 | Or a bare minimum **config.json** in the */cache* folder: 107 | ``` 108 | { 109 | "page_title" : "Your Blog Title", 110 | "page_sub" : "Tagline of Your Blog", 111 | "sites_enabled": { 112 | "example.com" : [] 113 | } 114 | } 115 | ``` 116 | And if testing for both *example.com* and locally on localhost, in **index.php** SITES_ENABLED: 117 | ``` 118 | { 119 | "example.com" : [], 120 | "localhost" : [] 121 | } 122 | ``` 123 | 124 | Or the **config.json** in the */cache* folder can be modified to the following: 125 | ``` 126 | { 127 | "page_title" : "Your Blog Title", 128 | "page_sub" : "Tagline of Your Blog", 129 | "sites_enabled": { 130 | "example.com" : [], 131 | "localhost" : [] 132 | } 133 | } 134 | ``` 135 | 136 | ### Multiple blogs or shared content 137 | Add your domains to SITES_ENABLED in *index.php* or to 'sites_enabled' in **config.json** as above: 138 | ``` 139 | { 140 | "example.com": [], 141 | "example.net": [] 142 | } 143 | ``` 144 | Add your posts for **example.com** in */posts/example.com/* and posts for **example.net** in */posts/example.net/* and so on, following the *year/month/day/title-slug.md* format. 145 | 146 | Hosting a blog in a subfolder instead of the main domain: 147 | ``` 148 | { 149 | "example.com" : [ 150 | { 151 | "basepath" : "\/myblog" 152 | } 153 | ] 154 | } 155 | ``` 156 | Or multiple blogs on the same domain: 157 | ``` 158 | { 159 | "example.com" : [ 160 | { 161 | "basepath" : "\/", 162 | "page_title" : "Example Main Blog", 163 | "page_sub" : "A Tagline for The Main Blog" 164 | }, 165 | { 166 | "basepath" : "\/secondblog", 167 | "page_title" : "Sub Example Blog", 168 | "page_sub" : "Sub Tagline for The Second Blog" 169 | } 170 | ] 171 | } 172 | ``` 173 | To put a blog into maintenance mode: 174 | ``` 175 | { 176 | "example.com" : [ 177 | { 178 | "basepath" : "\/", 179 | "is_maintenance": 1 180 | } 181 | ] 182 | } 183 | ``` 184 | Make a blog inactive: 185 | ``` 186 | { 187 | "example.com" : [ 188 | { 189 | "basepath" : "\/", 190 | "is_active": 0 191 | } 192 | ] 193 | } 194 | ``` 195 | As above, remember to escape forward slashes. 196 | 197 | Bare supports caching of formatted posts. Simply enable write permissions to the cache directory. On \*nix systems: 198 | ``` 199 | chmod -R 755 /your-web-root/cache 200 | ``` 201 | 202 | And then, follow the conventions in the example post: 203 | ``` 204 | /posts/2018/09/22/a-new-post.md 205 | ``` 206 | 207 | ## Content formatting 208 | Bare understands a few HTML tags, which can be extended, and a small subset of [Markdown](https://daringfireball.net/projects/markdown/). 209 | 210 | To bold or italicize text: 211 | ``` 212 | **bold text** *italic text* 213 | ``` 214 | 215 | To create a link: 216 | ``` 217 | [link text](https://example.com) 218 | ``` 219 | 220 | To embed a previously uploaded image file: 221 | ``` 222 | ![alt text](https://example.com/filename.jpg) 223 | or 224 | ![alt text](/path/to/filename.jpg) 225 | ``` 226 | 227 | To create a footnote: 228 | ``` 229 | This post has a footnote[^1] and another[^2] 230 | 231 | Footnotes are fully written at the end of the post body 232 | 233 | [^1]: First footnote 234 | [^2]: Second footnote with a [link](https://example.com) 235 | ``` 236 | Bare also supports text-based footnote references: 237 | ``` 238 | This post has a footnote[^One] and another[^Two] 239 | 240 | Footnotes are fully written at the end of the post body 241 | 242 | [^One]: First footnote 243 | [^Two]: Second footnote with a [link](https://example.com) 244 | ``` 245 | 246 | HTML is filtered of potentially harmful tags, however embedding videos to YouTube, Vimeo, PeerTube, Archive.org, LBRY/Odysee, or Playeur/Utreon is supported via shortcodes. 247 | 248 | E.G. For uploaded audio files: 249 | ``` 250 | [audio /path/to/file.mp3] 251 | ``` 252 | For uploaded video media: 253 | ``` 254 | Plain video: 255 | [video /path/to/media.mp4] 256 | 257 | Video with preview thumbnail: 258 | [video (/path/to/preview.jpg) /path/to/media.mp4] 259 | ``` 260 | For media with subtitles or captions in [WebVTT](https://en.wikipedia.org/wiki/WebVTT) format 261 | ``` 262 | Simple captions: 263 | [video [/path/to/captions.vtt] /path/to/media.mp4] 264 | 265 | Captions and media preview: 266 | [video [/path/to/captions.vtt] (/path/to/preview.jpg) /path/to/media.mp4] 267 | 268 | Subtitles: 269 | [video [lang=en&label=English&src=/path/to/en.vtt] /path/to/media.mp4] 270 | 271 | Subtitles in multiple languages: 272 | [video [lang=de&label=Deutsche&src=/path/to/de.vtt][lang=jp&label=日本語&src=/path/to/jp.vtt] /path/to/media.mp4] 273 | 274 | Multiple languages with single default set: 275 | [video [lang=en&label=English&default=yes&src=/path/to/en.vtt][lang=de&label=Deutsche&src=/path/to/de.vtt][lang=jp&label=日本語&src=/path/to/jp.vtt] /path/to/media.mp4] 276 | ``` 277 | 278 | For YouTube: 279 | ``` 280 | [youtube https://www.youtube.com/watch?v=RJ0ULhVKwEI] 281 | or 282 | [youtube https://youtu.be/RJ0ULhVKwEI] 283 | or 284 | [youtube RJ0ULhVKwEI] 285 | ``` 286 | For Vimeo: 287 | ``` 288 | [vimeo https://vimeo.com/113315619] 289 | or 290 | [vimeo 113315619] 291 | ``` 292 | For Archive.org: 293 | ``` 294 | [archive https://archive.org/details/A-Few-Days-In-Winter] 295 | or 296 | [archive A-Few-Days-In-Winter] 297 | ``` 298 | For PeerTube (any instance): 299 | ``` 300 | [peertube https://peertube.mastodon.host/videos/watch/56047136-00eb-4296-afc3-dd213fd6bab0] 301 | ``` 302 | **Note:** Remember to add the PeerTube instance URL to the list of URLs in FRAME_WHITELIST (one per line) in your *index.php* or 'frame_whitelist' in *config.json* (JSON array format) 303 | 304 | For Odysee or LBRY video (use the "Download" link in the share options): 305 | ``` 306 | [odysee https://odysee.com/$/download/eevblog-1367-5-types-of-oscilloscope/2d70c817aa4e1f7ce6b66473b0c3b66fd09d9281] 307 | or 308 | [lbry https://lbry.tv/$/download/eevblog-1367-5-types-of-oscilloscope/2d70c817aa4e1f7ce6b66473b0c3b66fd09d9281] 309 | ``` 310 | 311 | For Playeur (Utreon rebranded): 312 | ``` 313 | [playeur https://playeur.com/v/gP3WmpEiTz2] 314 | ``` 315 | Older embedded Utreon videos already in posts will still work as-is, as long as the original videos still exist on the platform. 316 | 317 | 318 | ## Custom errors (optional) 319 | 320 | A separate */errors* folder can be used to create custom error pages for each HTTP status code. The location of the */errors* folder is configurable via the ERROR_ROOT setting. 321 | 322 | E.G. Create an */errors/404.html* file and put your custom Not Found content. The following status codes are supported for custom errors: 323 | `400, 401, 403, 404, 405, 429, 500, 501, 503` 324 | 325 | 326 | ## Installing on other web servers 327 | 328 | ### Nginx 329 | 330 | The Nginx web server supports URL rewriting and file filtering. The following is a simple configuration for a site named example.com. 331 | **Note:** The pound sign(#) denotes comments. 332 | 333 | The following is an example server block tested on Manjaro Linux (Arch based). The location of **nginx.config** will depend on your platform. 334 | ``` 335 | server { 336 | server_name example.com; 337 | 338 | # Change this to your web root, if it's different 339 | root /usr/share/nginx/example.com/html; 340 | 341 | # Prevent access to special files 342 | location ~\.(hta|htp|md|conf|db|sql|json|sh)\$ { 343 | deny all; 344 | } 345 | 346 | # Prevent access to cache folder 347 | location /cache { 348 | deny all; 349 | } 350 | 351 | # Remember to put static files (I.E. .css, .js etc...) 352 | # in the same directory you set in FILE_PATH 353 | 354 | # Send all requests (that aren't static files) to index.php 355 | location / { 356 | try_files $uri @barehandler; 357 | index index.php; 358 | } 359 | 360 | location @barehandler { 361 | rewrite ^(.*)$ /index.php; 362 | } 363 | 364 | # Handle php 365 | location ~ \.php$ { 366 | fastcgi_pass unix:/run/php-fpm/php-fpm.sock; 367 | fastcgi_index index.php; 368 | include fastcgi.conf; 369 | } 370 | } 371 | ``` 372 | 373 | The *index.php* file should be in the subfolder you're hosting the blog from, if you haven't added any custom rewriting rules to your webserver configuration, and also remember to change the static file and cache directory. This is especially important when hosting a blog from a subfolder instead of the main web root. 374 | 375 | ### OpenBSD's httpd(8) web server 376 | 377 | The OpenBSD operating system comes with its own web server in the base installation. Previously, this was the Apache web server and then Nginx. 378 | 379 | OpenBSD does not come with PHP and needs to be installed separately. Select the highest version after each command: 380 | ``` 381 | doas pkg_add php 382 | doas pkg_add php-pdo_sqlite 383 | doas pkg_add php-tidy 384 | doas pkg_add php-intl 385 | ``` 386 | If you're already logged in as root, skip the "[doas](https://man.openbsd.org/doas)" before each command. 387 | 388 | Edit **/etc/php-8.x.ini** (or the version of PHP you're running) and make sure the following extensions are enabled. 389 | ``` 390 | extension=fileinfo 391 | extension=intl 392 | extension=mbstring 393 | extension=pdo_sqlite 394 | extension=sqlite3 395 | extension=tidy 396 | ``` 397 | 398 | Now enable and start PHP. 399 | This is assuming 8.1, but other versions follow the same convention: 400 | ``` 401 | doas rcctl enable php82_fpm 402 | doas rcctl start php82_fpm 403 | ``` 404 | 405 | **Note:** Although it shares the same comment style, httpd(8) [configuration](https://man.openbsd.org/httpd.conf.5) directives *do not* end in a semicolon(;) unlike Nginx settings. 406 | 407 | The following configuration can be used if Bare is installed as the "example.com" website (tested on OpenBSD 7.5). 408 | 409 | Edit **/etc/httpd.conf** to add a custom server setting file: 410 | ``` 411 | include "/etc/httpd.conf.local" 412 | ``` 413 | 414 | Create **/etc/httpd.conf.local** and add the following: 415 | ``` 416 | # A site called "example.com" 417 | server "www.example.com" { 418 | alias "example.com" 419 | 420 | # listening on external addresses 421 | listen on egress port 80 422 | 423 | # Uncomment the following, if you're only hosting on Tor 424 | #listen on lo port 80 425 | 426 | # Default directory 427 | directory index "index.html" 428 | 429 | # Change this to your web root, if it's different 430 | root "/htdocs" 431 | 432 | # Prevent access to special files 433 | location "/*.hta*" { block } 434 | location "/*.htp*" { block } 435 | location "/*.md*" { block } 436 | location "/*.conf*" { block } 437 | location "/*.db*" { block } 438 | location "/*.sql*" { block } 439 | location "/*.json*" { block } 440 | location "/*.sh*" { block } 441 | 442 | # Prevent access to data folder 443 | location "/cache/*" { block } 444 | 445 | # Remember to put static files (I.E. .css, .js etc...) 446 | # In the same directory you set in FILE_PATH 447 | 448 | # Let index.php handle all other requests 449 | location "/*" { 450 | directory index "index.php" 451 | 452 | # Change this to your web root, if it's different 453 | root { "/htdocs/index.php" } 454 | 455 | # Enable FastCGI handling of PHP 456 | fastcgi socket "/run/php-fpm.sock" 457 | } 458 | } 459 | ``` 460 | Check your configuration by running: 461 | ``` 462 | httpd -n 463 | ``` 464 | 465 | If there are no errors, run the following to load the changes: 466 | ``` 467 | doas rcctl reload httpd 468 | ``` 469 | 470 | Your new Bare blog is ready to be served. 471 | 472 | To ensure the server and PHP will run each time you (re)start your OpenBSD system, remember to add the following to **rc.conf.local**: 473 | ``` 474 | httpd_flags= 475 | pkg_scripts=php82_fpm 476 | ``` 477 | 478 | Or if you're hosting your blog over Tor, make sure that's added as well: 479 | ``` 480 | httpd_flags= 481 | pkg_scripts=tor php82_fpm 482 | ``` 483 | 484 | If you have already enabled firewall access to your site, you may skip this part. 485 | 486 | The OpenBSD [firewall is pf](https://man.openbsd.org/pf), which requires its own set of directives to enable serving websites. There is an example pf configuration which will let external web traffic through. Study the manual for a more detailed explanation what each of these directives do. 487 | 488 | Make a backup of **/etc/pf.conf** first and include these directives: 489 | ``` 490 | # This may be adjusted for an e-commerce site, with shopping carts etc.., but this is fine for a Blog 491 | set limit { states 500000, frags 2000 } 492 | 493 | # A table to store flooding clients 494 | table persist counters 495 | 496 | # You can also create permanent tables and add IP addresses to them 497 | # E.G. A blocklist called "blocked" in a folder called "pftables" 498 | # table persist file "/etc/pftables/blocked" 499 | 500 | # Web traffic serving with maximum connection rate and throttling 501 | websrv="(max 500, source-track rule, max-src-states 50, max-src-conn-rate 500/5, \ 502 | max-src-conn 50, overload flush global)" 503 | 504 | # If you're also receiving email on the same server, uncomment the following 505 | #mailsrv="(max 500, source-track rule, max-src-states 5, max-src-conn-rate 5/10, \ 506 | # max-src-conn 3, overload flush global)" 507 | 508 | # Note the slash at the end of the first line indicates the directive wraps 509 | # DO NOT add a space after that slash 510 | 511 | # Block policy is to just drop the connection by default 512 | set block-policy drop 513 | 514 | # Ignore loopback (localhost) 515 | set skip on lo 516 | 517 | # Accommodate slow clients (E.G. when hosting over Tor) 518 | set optimization high-latency 519 | set ruleset-optimization profile 520 | set timeout { frag 30 } 521 | 522 | # Set syn cookies 523 | set syncookies adaptive (start 25%, end 12%) 524 | 525 | # Safety scrub 526 | match in all scrub (no-df random-id max-mss 1440) 527 | match out all scrub (no-df random-id reassemble tcp max-mss 1440) 528 | 529 | # This *mostly* works, but a dedicated blocklist, like Spamhaus, is strongly recommended 530 | antispoof quick for { egress lo0 } 531 | block quick from { } 532 | 533 | # You can add more comma delimited tables to that list 534 | # E.G. , , etc... 535 | 536 | # Deny access in both directions by default 537 | block all 538 | block return 539 | 540 | # Spoof protection 541 | block in quick from urpf-failed to any 542 | block in quick from no-route to any 543 | 544 | # Allow access to web ports (email is similar, but outside the scope of this) 545 | pass in on egress inet proto tcp from any to (egress) port { 80 443 } keep state $websrv 546 | 547 | # Uncomment this line if you're also handling email on this server 548 | #pass in on egress inet proto tcp from any to (egress) port { 25 } keep state $mailsrv 549 | 550 | # Pass TCP, UDP, ICMP 551 | pass out on egress proto { tcp, udp, icmp } all modulate state 552 | 553 | # The pf.conf that comes with OpenBSD has some other settings, which should be left as-is 554 | # Only add/modify what's needed to get your site up and running, but learn more about what these do 555 | 556 | ``` 557 | 558 | ## Running a TLS-enabled Bare blog on OpenBSD 559 | **Note:** Remember to point your DNS servers to your web server's public IP address first. 560 | 561 | This assumes you're already logged in as root and skips "doas". PHP installation is the same as above. 562 | 563 | The server configuration is similar at first to the above steps. 564 | Edit **/etc/httpd.conf** to include your custom server settings: 565 | ``` 566 | include "/etc/httpd.conf.local" 567 | ``` 568 | 569 | Create or edit **/etc/httpd.conf.local** same as above, but add this instead: 570 | ``` 571 | # A site called "example.com" 572 | server "www.example.com" { 573 | alias "example.com" 574 | 575 | # listening on external addresses on port 80 576 | listen on egress port 80 577 | 578 | # Default directory 579 | directory index "index.html" 580 | 581 | # Change this to your web root, if it's different 582 | root "/htdocs" 583 | 584 | # acme-client needs this 585 | location "/.well-known/acme-challenge/*" { 586 | root "/acme" 587 | request strip 2 588 | } 589 | } 590 | ``` 591 | OpenBSD comes with acme-client and this creates a basic website to use it. 592 | 593 | Create or edit **/etc/acme-client.conf** to make sure it contains this: 594 | ``` 595 | authority letsencrypt { 596 | api url "https://acme-v02.api.letsencrypt.org/directory" 597 | account key "/etc/ssl/private/letsencrypt.pem" 598 | } 599 | 600 | # Substitute example.com for your own domain 601 | domain example.com { 602 | alternative names { www.example.com } 603 | domain key "/etc/ssl/private/example.com.key" 604 | domain certificate "/etc/ssl/example.com.crt" 605 | domain full chain certificate "/etc/ssl/example.com.pem" 606 | sign with letsencrypt 607 | } 608 | 609 | # And so on for your other domains... 610 | ``` 611 | 612 | Create these folders with appropriate permissions: 613 | ``` 614 | mkdir -p -m 700 /etc/ssl/private 615 | mkdir -p -m 755 /var/www/acme 616 | ``` 617 | 618 | Check your configurations: 619 | ``` 620 | httpd -n 621 | acme-client -n 622 | ``` 623 | 624 | If there are no errors, reload httpd(8) so it can recognize changes: 625 | ``` 626 | rcctl reload httpd 627 | ``` 628 | 629 | Then, run acme client to get your new certificate: 630 | ``` 631 | acme-client example.com 632 | ``` 633 | 634 | Your new certificate should be created and ready to be used. 635 | 636 | Now, go back to **/etc/httpd.conf.local** to add the new settings. This is an example of a TLS enabled site with full logging: 637 | ``` 638 | # A site called "example.com" 639 | 640 | # Redirect all non-TLS requests to TLS enabled site 641 | server "www.example.com" { 642 | alias "example.com" 643 | 644 | # listening on external addresses on port 80 645 | listen on egress port 80 646 | block return 301 "https://example.com$REQUEST_URI" 647 | } 648 | 649 | server "www.example.com" { 650 | alias "example.com" 651 | 652 | # listening on external addresses on TLS port 653 | listen on egress tls port 443 654 | 655 | # Default directory 656 | directory index "index.html" 657 | 658 | # Logging for access and errors 659 | # Note: this is for request errors, which are different from blog errors 660 | log access "/example.com/access.log" 661 | log error "/example.com/error.log" 662 | 663 | # Change this to your web root, if it's different 664 | root "/htdocs" 665 | 666 | # Create your certificates first 667 | hsts max-age 31536000 668 | hsts subdomains 669 | tls { 670 | certificate "/etc/ssl/example.com.pem" 671 | key "/etc/ssl/private/example.com.key" 672 | } 673 | 674 | # This is specific to acme-client 675 | location "/.well-known/acme-challenge/*" { 676 | root "/acme" 677 | request strip 2 678 | } 679 | 680 | # Rest is the same as before 681 | 682 | # Prevent access to special files 683 | location "/*.hta*" { block } 684 | location "/*.htp*" { block } 685 | location "/*.md*" { block } 686 | location "/*.conf*" { block } 687 | location "/*.db*" { block } 688 | location "/*.sql*" { block } 689 | location "/*.json*" { block } 690 | location "/*.sh*" { block } 691 | 692 | # Prevent access to data folder, if it's also in the web root 693 | location "/cache/*" { block } 694 | 695 | # Remember to put static files (I.E. .css, .js etc...) 696 | # In the same directory you set in FILE_PATH 697 | 698 | # Let index.php handle all other requests 699 | location "/*" { 700 | directory index "index.php" 701 | 702 | # Change this to your web root, if it's different 703 | root { "/htdocs/index.php" } 704 | 705 | # Enable FastCGI handling of PHP 706 | fastcgi socket "/run/php-fpm.sock" 707 | } 708 | } 709 | ``` 710 | 711 | It's a good idea to create a subfolder for each domain for logging 712 | ``` 713 | mkdir -p -m 755 /var/www/logs/example.com 714 | ``` 715 | 716 | The TLS settings have been made. Check the configuration again: 717 | ``` 718 | httpd -n 719 | ``` 720 | 721 | If there are no errors, reload httpd(8) to launch the site: 722 | ``` 723 | rcctl reload httpd 724 | ``` 725 | 726 | To make sure your certificate renews automatically, create or edit 727 | **/etc/weekly.local** with: 728 | ``` 729 | acme-client example.com 730 | ``` 731 | Now, every week, your domain's certificates are renewed when it's getting close to expiration. 732 | 733 | Your Bare blog will now be served over a secure connection. 734 | 735 | ## Troubleshooting 736 | 737 | Always remember to backup *index.php* before making modifications to it and also *config.json* if one was created in the **/cache** folder. Remember that some plugins may cause errors. Disabling any newly added plugins (by taking them off the "plugins_enabled" list in *config.json* or removing them from the PLUGINS_ENABLED setting in *index.php*) may fix a problem. 738 | 739 | These are possible problems you may encounter and potential solutions to them. If Bare was able to run at least once, check the *error.log* file in the CACHE folder to see if something was written there. Most of the time, if Bare was succssfully installed, this will give you some useful information. 740 | 741 | * **Problem: I don't see anything** 742 | * Check if you have set all the required definitions in *index.php* such as PATH, CACHE, and POSTS. If you only have the demo post and no plugins installed yet, Bare should run with just the default settings for the rest. 743 | 744 | * Check if you have uploaded the posts folder with at least one post and cache folder along with *index.php*. For the Apache webserver, the .htaccess file is also required. 745 | 746 | * Check if you have any JSON errors in *config.json* if you're using a custom configuration like this. Bare doesn't need a *config.json*, but having one is recommended as it makes upgrading easier. 747 | 748 | * Check if your webserver is configured to host your site, have set permissions (chmod -R 0755 on \*nix systems) on the CACHE folder, and PHP has permission to run following the [installation instructions](#installation). 749 | 750 | * At a minimum, check if you have PHP enabled and, as a test, create a plain *index.php* file with just ` 2 | 3 | 4 | 5 | 6 | 7 | 403 8 | 9 | 10 | 11 |
12 |

403

13 |

Access to this resource is restricted.

14 |

Return home.

15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /errors/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 404 8 | 9 | 10 | 11 |
12 |

404

13 |

The page or resource you're looking for cannot be found.

14 |

Return home.

15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /posts/2018/09/22/a-new-post.md: -------------------------------------------------------------------------------- 1 | A new post 2 | 3 | This is a post in your /posts directory. 4 | 5 | The slug is the file name minus the file extension. Posts should end in .md so Bare knows it's a post. 6 | 7 | Bare supports some minimal [fomatting](https://github.com/cypnk/Bare). 8 | 9 | tags: first post, hello 10 | -------------------------------------------------------------------------------- /posts/about/main.md: -------------------------------------------------------------------------------- 1 | About Page 2 | 3 | This is an example about page on your blog. The first line is the title and the rest can be *formatted* just like a regular post 4 | 5 | Return [home](/). 6 | -------------------------------------------------------------------------------- /posts/error.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box 3 | } 4 | 5 | body { 6 | font: 400 1rem sans-serif; 7 | line-height: 1.6; 8 | color: #34495E; 9 | background: #efefef 10 | } 11 | 12 | h1 { 13 | font-weight: 400; margin: 0; 14 | } 15 | 16 | a { color: #415b76 } 17 | a:active { color: #e74c3c } 18 | a:hover{ color: #2c81ba } 19 | 20 | header { 21 | display: none; 22 | } 23 | 24 | main { 25 | position: absolute; 26 | width: 80%; 27 | top: 50%; 28 | left: 50%; 29 | transform: translate( -50%, -50% ) 30 | } 31 | -------------------------------------------------------------------------------- /posts/style.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /** 4 | * Bare default theme https://github.com/cypnk/Bare 5 | */ 6 | * { 7 | margin: 0; 8 | padding: 0; 9 | border: 0; 10 | box-sizing: border-box; 11 | } 12 | 13 | html { 14 | font-size: 100%; 15 | } 16 | 17 | article, aside, footer, figure, figcaption, details, header, main, nav, summary, section, blockquote { 18 | display: block; 19 | } 20 | 21 | audio:not([controls]) { 22 | display: none; 23 | height: 0 24 | } 25 | 26 | body { 27 | font: 400 1rem sans-serif; 28 | line-height: 1.8rem; 29 | color: #37474f; 30 | background: #f7f7fa; 31 | text-rendering: optimizeLegibility; 32 | text-rendering: geometricPrecision; 33 | font-smooth: always; 34 | font-smoothing: antialiased; 35 | } 36 | 37 | h1, h2, h3, h4, h5, h6 { 38 | margin: 2rem 0 .5rem 0; 39 | font-family: Georgia, serif; 40 | line-height: 1.4; 41 | } 42 | 43 | header h1 { 44 | margin-bottom: 0; 45 | font-size: 2rem; 46 | } 47 | 48 | h1, h2, h3 { font-weight: 400; } 49 | h1 { font-size: 2.55rem; } 50 | h2 { font-size: 2.25rem; } 51 | header h2, h3 { font-size: 1.5rem; } 52 | h4 { font-size: 1.25rem; } 53 | h5 { font-size: 1rem; } 54 | h6 { font-size: .875rem; } 55 | 56 | header h2 { 57 | color: #555; 58 | margin-bottom: 0; 59 | } 60 | 61 | p { margin: 1rem 0; } 62 | 63 | header p { margin: 0 } 64 | header p + p { margin-top: 1rem; } 65 | article p + p { margin-top: 2rem; } 66 | 67 | header p.links { text-align: center; } 68 | 69 | header { 70 | text-align: center; 71 | color: #2c3e50; 72 | } 73 | 74 | main article:first-child, article + article, footer { 75 | margin-top: 2rem; 76 | border-top: 1px solid #ddd; 77 | } 78 | 79 | article + article { 80 | border-width: 2px 0 0 0; 81 | border-style: dashed; 82 | } 83 | 84 | a { 85 | text-decoration: underline; 86 | text-decoration-skip: ink; 87 | color: #2c3e50; 88 | } 89 | 90 | time, header .readtime { 91 | text-transform: uppercase; 92 | font-size: .9rem; 93 | letter-spacing: .07rem; 94 | color: #607d8b; 95 | } 96 | 97 | header .readtime:before { 98 | content: ' | '; 99 | } 100 | 101 | footer { 102 | font-size: .875rem; 103 | text-align: center; 104 | padding: 1rem 0; 105 | } 106 | 107 | header a, header a:visited { 108 | text-decoration-skip: none; 109 | color: #2c3e50; 110 | } 111 | 112 | article header a, header h1 a { 113 | text-decoration: none; 114 | } 115 | 116 | .content { 117 | padding:0 1rem; 118 | margin: 0 auto; 119 | max-width: 45rem; 120 | } 121 | 122 | .cover { background-size: cover !important; } 123 | .contain { background-size: contain !important; } 124 | 125 | .full, .media { 126 | width: 100%; 127 | display: block; 128 | } 129 | 130 | .media { 131 | text-align: center; 132 | margin: 2rem auto; 133 | } 134 | 135 | hr { 136 | box-sizing: content-box; 137 | height: 0; 138 | overflow: visible; 139 | } 140 | 141 | code, kbd, pre, samp { 142 | font-family: monospace,serif; 143 | font-size: 1em 144 | } 145 | 146 | code { 147 | background: #f5f5f5; 148 | } 149 | 150 | pre { 151 | display: block; 152 | margin: 1rem 0; 153 | overflow: scroll; 154 | } 155 | 156 | pre code { 157 | margin: 0; 158 | display: block; 159 | padding: 1rem; 160 | } 161 | 162 | img, a img { 163 | max-width: 100%; border: none; 164 | } 165 | 166 | article img, article figure { 167 | display: block; 168 | height: auto; 169 | width: 100%; 170 | padding: .5rem; 171 | border: 1px solid #dcdcdc; 172 | margin: 1em auto; 173 | } 174 | 175 | article figure img { 176 | border: 0; 177 | padding: 0; 178 | margin: 0; 179 | } 180 | 181 | figcaption { 182 | font-style: italic; 183 | font-size: 90%; 184 | margin: 1rem 0; 185 | line-height: 1.3em; 186 | } 187 | 188 | .center { 189 | display: block; 190 | text-align: center 191 | } 192 | 193 | ul, ol { 194 | margin: 1em 0 1em 2em; 195 | line-height: 1.5em; 196 | } 197 | 198 | ul li, ol li { 199 | padding: .2rem .5rem; 200 | } 201 | 202 | nav ul li { 203 | display: inline-block; 204 | padding: 0; 205 | margin: 0; 206 | } 207 | 208 | nav ul li a { 209 | padding: .3rem .4rem; 210 | } 211 | 212 | nav.main { 213 | text-align: center; 214 | } 215 | 216 | nav.siblings, nav.related, nav.related ul li + li { 217 | margin-top: 1rem; 218 | } 219 | 220 | nav.siblings ul li + li { 221 | float: right; 222 | } 223 | 224 | nav.related ul li { 225 | display: block; 226 | } 227 | 228 | nav.tags { 229 | line-height: 1; 230 | font-size: 90%; 231 | } 232 | 233 | article nav.tags { 234 | text-align: right; 235 | } 236 | 237 | nav ul { 238 | margin: 0; 239 | padding: 0; 240 | list-style: none; 241 | } 242 | 243 | nav.tags ul { 244 | display: inline-block; 245 | } 246 | 247 | nav.tags ul li { 248 | margin: 0; 249 | } 250 | 251 | nav.tags ul li + li { 252 | margin-left: 1rem; 253 | } 254 | 255 | nav.footnotes { 256 | margin-top: 1rem; 257 | padding-top: 1rem; 258 | border-top: 1px solid #dcdcdc; 259 | } 260 | 261 | nav.footnotes ul { 262 | margin-left: 1rem; 263 | } 264 | 265 | nav.footnote ul li { 266 | display: block; 267 | } 268 | 269 | nav.footnotes ul li + li { 270 | padding-top: 0.5em; 271 | } 272 | 273 | nav.footnote ul li a { 274 | padding: 0; 275 | display: inline-block; 276 | } 277 | 278 | ul.index { 279 | margin: 1rem 0; 280 | list-style: none; 281 | margin-bottom: 1rem; 282 | } 283 | 284 | ul.index li + li { 285 | margin-top: 1rem; 286 | } 287 | 288 | ul.index li time, ul.index li .readtime { 289 | display: block; 290 | } 291 | 292 | ul.index li .readtime { 293 | font-size: 90%; 294 | } 295 | 296 | ul.index li nav.tags ul, ul.index li nav.tags ul li { 297 | margin: 0; 298 | } 299 | 300 | nav.main ul { 301 | display: inline-block; 302 | margin: 0 auto; 303 | } 304 | 305 | ul.index li nav ul { 306 | margin-top: 0; 307 | padding: 0; 308 | } 309 | 310 | blockquote { 311 | margin: 0 auto; 312 | width: 80%; 313 | color: #546e7a; 314 | } 315 | 316 | form { 317 | margin: .5rem 0; 318 | } 319 | 320 | input { 321 | padding: .2rem 1rem; 322 | font: inherit; 323 | color: inherit; 324 | line-height: inherit; 325 | } 326 | 327 | input[type="submit"] { 328 | cursor: pointer; 329 | background: #dcdcdc; 330 | border: 1px solid #dcdcdc; 331 | } 332 | 333 | input[type="search"] { 334 | appearance: textfield; 335 | background: #fff; 336 | border: 1px solid #dcdcdc; 337 | } 338 | 339 | header input[type="search"] { 340 | min-width: 50%; 341 | } 342 | 343 | 344 | @media screen and ( max-width: 800px ) { 345 | 346 | } 347 | 348 | @media screen and ( max-width: 640px ) { 349 | 350 | } 351 | 352 | @media screen and ( max-width: 400px ) { 353 | 354 | } 355 | 356 | 357 | --------------------------------------------------------------------------------