├── .gitignore
├── entrypoint.sh
├── keys
└── readme.txt
├── go.mod
├── Dockerfile
├── types.go
├── general-motd.txt
├── db.go
├── LICENSE.md
├── go.sum
├── README.md
├── docs
└── index.html
├── api.go
└── main.go
/.gitignore:
--------------------------------------------------------------------------------
1 | blacklist.txt
2 | general-motd.txt
3 | whitelist.txt
4 | shhhbb
5 | *.db
6 | BUILDS
7 | keys/ssh*
--------------------------------------------------------------------------------
/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | mkdir -p /keys
4 | [[ ! -f /keys/ssh_host_ed25519_key ]] && ssh-keygen -t ed25519 -f /keys/ssh_host_ed25519_key -N ""
5 |
6 | exec "$@"
--------------------------------------------------------------------------------
/keys/readme.txt:
--------------------------------------------------------------------------------
1 | hello :) you might be here because you started the bbs and it complained that you didn't have any keys!
2 | place your keys in this directory. if you dont have keys, you can generate some with this command:
3 |
4 | ssh-keygen -t ed25519 -C "my shhhbb host key"
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/donuts-are-good/shhhbb
2 |
3 | go 1.20
4 |
5 | require (
6 | github.com/jmoiron/sqlx v1.3.5
7 | github.com/mattn/go-sqlite3 v1.14.16
8 | golang.org/x/crypto v0.7.0
9 | golang.org/x/term v0.6.0
10 | )
11 |
12 | require golang.org/x/sys v0.6.0 // indirect
13 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.20 AS builder
2 |
3 |
4 | WORKDIR /src
5 | COPY . .
6 |
7 | RUN CGO_ENABLED=1 GOOS=linux go build -o /app
8 |
9 |
10 | FROM ubuntu
11 |
12 | RUN apt-get update && apt-get -y install openssh-client
13 |
14 | COPY --from=builder /app /app
15 | COPY ./entrypoint.sh ./entrypoint.sh
16 |
17 | EXPOSE 2223
18 |
19 | ENTRYPOINT [ "./entrypoint.sh"]
20 | CMD [ "/app", "2223" ]
--------------------------------------------------------------------------------
/types.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "container/list"
5 | "sync"
6 |
7 | "golang.org/x/crypto/ssh"
8 | )
9 |
10 | var users = make(map[string]*user)
11 | var usersMutex sync.Mutex
12 | var messageCache *list.List
13 | var semverInfo = "v0.4.0"
14 | var motdFilePath = "./general-motd.txt"
15 |
16 | type user struct {
17 | Pubkey string `json:"pubkey" db:"pubkey"`
18 | Hash string `json:"hash" db:"hash"`
19 | Conn ssh.Channel `json:"-"`
20 | Ignored map[string]bool `json:"-"`
21 | }
22 |
23 | type discussion struct {
24 | ID int `json:"id" db:"id"`
25 | Author string `json:"author" db:"author"`
26 | Message string `json:"message" db:"message"`
27 | Replies []*reply `json:"replies"`
28 | }
29 |
30 | type reply struct {
31 | Author string `json:"author" db:"author"`
32 | Message string `json:"message" db:"message"`
33 | }
34 |
--------------------------------------------------------------------------------
/general-motd.txt:
--------------------------------------------------------------------------------
1 | ==============================================
2 | = _ _ _ _ _ =
3 | = | |__ _ _ | || | ___ | |_ (_) _ __ =
4 | = | '_ \ | | | || || | / _ \| __|| || '_ \ =
5 | = | |_) || |_| || || || __/| |_ | || | | | =
6 | = |_.__/ \__,_||_||_| \___| \__||_||_| |_| =
7 | ==============================================
8 |
9 | Announcements:
10 |
11 | v0.4.0
12 | - test server is up! `ssh -p 2224 shhhbb.com`
13 | - api comments: https://github.com/donuts-are-good/shhhbb/issues/8
14 | - added API auth tokens
15 | - tokens List
16 | - tokens new
17 | - tokens revoke
18 | - added api
19 | - api has chat
20 | - api has board discussions
21 |
22 | v0.3.1
23 | - check it out we have MOTD's now :D
24 | - added /q, /quit, /x, /exit
25 | - added /?
26 | - added filtering for / commands in chat
27 | - added /motd, /bulletin
28 | - fixed chat scrolling disrupting input
29 | - added /history to reload the last 100 chat lines
30 |
31 |
32 |
--------------------------------------------------------------------------------
/db.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 |
6 | "github.com/jmoiron/sqlx"
7 | )
8 |
9 | func initSqliteDB() *sqlx.DB {
10 | db, err := sqlx.Connect("sqlite3", "board.db")
11 | if err != nil {
12 | log.Fatalln(err)
13 | }
14 | return db
15 | }
16 |
17 | func initBoardSchema(db *sqlx.DB) {
18 | schema := `
19 | CREATE TABLE IF NOT EXISTS discussions (
20 | id INTEGER PRIMARY KEY,
21 | author TEXT NOT NULL,
22 | message TEXT NOT NULL
23 | );
24 |
25 | CREATE TABLE IF NOT EXISTS replies (
26 | id INTEGER PRIMARY KEY,
27 | discussion_id INTEGER NOT NULL,
28 | author TEXT NOT NULL,
29 | message TEXT NOT NULL,
30 | FOREIGN KEY (discussion_id) REFERENCES discussions(id) ON DELETE CASCADE
31 | );
32 | `
33 | _, err := db.Exec(schema)
34 | if err != nil {
35 | log.Fatalln(err)
36 | }
37 | }
38 |
39 |
40 | func createReply(db *sqlx.DB, postID int, authorHash string, replyBody string) error {
41 | _, err := db.Exec("INSERT INTO replies (discussion_id, author, message) VALUES (?, ?, ?)", postID, authorHash, replyBody)
42 | return err
43 | }
44 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 | Copyright (c) 2023 donuts-are-good https://github.com/donuts-are-good
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 | The above copyright notice and this permission notice shall be included in all
10 | copies or substantial portions of the Software.
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17 | SOFTWARE.
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
2 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
3 | github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
4 | github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
5 | github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
6 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
7 | github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
8 | github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
9 | github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
10 | golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
11 | golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
12 | golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
13 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
14 | golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
15 | golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |   
4 |
5 | # shhhbb
6 | ssh based BBS & chat over SSH
7 |
8 |
11 |
12 | 
13 |
14 |
15 |
16 | **instructions:**
17 | 1. create a directory called `./keys`
18 | 2. generate an ed25519 keypair in there without password
19 | `ssh-keygen -t ed25519 -C "my cool keypair" -f ./keys/ssh_host_ed25519_key`
20 | 3. launch with `./shhhbb 2223` where `2223` is the port
21 |
22 | ## api
23 |
24 | the api is designed to allow users to create and retrieve chat messages and posts. it is secured with token-based authentication using bearer tokens.
25 |
26 | ### base url
27 |
28 | http://localhost:8080
29 |
30 | ### authentication
31 | all endpoints require authentication with a bearer token. to obtain a bearer token, the user must first log in and then authenticate themselves with their token in subsequent requests.
32 |
33 | ```
34 | /token new
35 | /token list
36 | /token revoke
37 | ```
38 |
39 | ### endpoints
40 |
41 | **get /chat/messages**
42 | *retrieve the last 100 chat messages.*
43 |
44 | - parameters: none
45 | - authentication: bearer token required
46 | - response: a json object with a boolean success field and an array data field containing objects with the following properties:
47 | - sender: the hash of the message sender
48 | - message: the message body
49 | - timestamp: the time the message was sent in iso 8601 format
50 |
51 | **post /chat/create**
52 | *create a new chat message.*
53 |
54 | - parameters:
55 | - sender_hash: the hash of the message sender
56 | - message: the message body
57 | - authentication: bearer token required
58 | - response: a json object with a boolean success field
59 |
60 | **post /chat/direct/create**
61 | *create a new direct message.*
62 |
63 | - parameters:
64 | - sender: the hash of the message sender
65 | - recipient: the hash of the message recipient
66 | - message: the message body
67 | - authentication: bearer token required
68 | - response: a json object with a boolean success field
69 |
70 | **get /posts/list**
71 | *retrieve a list of posts.*
72 |
73 | - parameters: none
74 | - authentication: bearer token required
75 | - response: a json object with a boolean success field and an array data field containing objects with the following properties:
76 | - post_id: the id of the post
77 | - author_hash: the hash of the post author
78 | - post_body: the post body
79 | - timestamp: the time the post was created in iso 8601 format
80 |
81 | **post /posts/create**
82 | *create a new post.*
83 |
84 | - parameters:
85 | - author_hash: the hash of the post author
86 | - post_body: the post body
87 | - authentication: bearer token required
88 | - response: a json object with a boolean success field
89 |
90 | **get /posts/replies**
91 | *retrieve a list of replies to a post.*
92 |
93 | - parameters:
94 | - post_id: the id of the post to retrieve replies for
95 | - authentication: bearer token required
96 | - response: a json object with a boolean success field and an array data field containing objects with the following properties:
97 | - reply_id: the id of the reply
98 | - post_id: the id of the post being replied to
99 | - author_hash: the hash of the reply author
100 | - reply_body: the reply body
101 | - timestamp: the time the reply was created in iso 8601 format
102 |
103 | **post /posts/reply**
104 | *create a new reply to a post.*
105 |
106 | - parameters:
107 | - post_id: the id of the post being replied to
108 | - author_hash: the hash of the reply author
109 | - reply_body: the reply body
110 | - authentication: bearer token required
111 | - response: a json object with a boolean success field
112 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
use your normal ssh client. you don't need to sign up, just sign in with ssh and the bbs does the rest.
47 |
Note: If you need to generate a key, you can use this command to generate a good modern key. ssh-keygen -t ed25519 -C "shhhbb.com bbs key"
48 |
ssh -p 2223 shhhbb.com
49 |
50 |
how to host your own bbs
51 |
using shhhbb to host your bbs means anybody with any ssh key can interact with the bbs. as such, it's wise to run as a non-privileged user on a non-critical server, like a vps.
2. put your host keys in shhhbb/keys directory.
56 | Note: if you need to generate a new key, try this: ssh-keygen -t ed25519 -C "shhhbb host key"
57 |
58 |
3. specify a port and run the server like this:
59 |
./shhhbb 2223
60 |
61 |
62 |
how to edit the code
63 |
the server is MIT licensed. if you don't know what that means, don't worry about it. but the important part of what that means for this program is you can make any changes you like. here are some short instructions to get the code and build it yourself. it's easy, don't worry.
2. make your changes and save the file. everything happens in main.go
68 |
3. build it. all you need installed is Go, which you can get here: https://golang.org
69 |
go build
70 |
4. Optional: i made a thing that will compile the server for every cpu and os it is compatible with, which is about 30-40 platforms. if you're into that, it's a simple bash tool you can try here: donuts-are-good/release.sh
71 |
./release.sh --name "shhhbb" --version "v0.0.2"
72 |
73 |
api
74 |
the api is designed to allow users to create and retrieve chat messages and posts. it is secured with token-based authentication using bearer tokens.
all endpoints require authentication with a bearer token. to obtain a bearer token, the user must first log in and then authenticate themselves with their token in subsequent requests.