docs: restructure

This commit is contained in:
Erik Michelson 2025-02-11 18:00:04 +01:00 committed by Erik Michelson
parent 91ebd519a8
commit 70a6583fe0
No known key found for this signature in database
GPG key ID: DB99ADDDC5C0AF82
69 changed files with 471 additions and 385 deletions

View file

@ -0,0 +1,57 @@
# API Authentication
!!! info "Design Document"
This is a design document, explaining the design and vision for a HedgeDoc 2
feature. It is not a user guide and may or may not be fully implemented.
## Public API
All requests to the public API require authentication using a [bearer token][bearer-token].
This token can be generated using the profile page in the frontend
(which in turn uses the private API to generate the token).
### Token generation
When a new token is requested via the private API, the backend generates a 64 bytes-long secret of
cryptographically secure data and returns it as a base64url-encoded string,
along with an identifier. That string can then be used by clients as a bearer token.
A SHA-512 hash of the secret is stored in the database. To validate tokens, the backend computes
the hash of the providedsecret and checks it against the stored hash for the provided identifier.
#### Choosing a hash function
Unfortunately, there does not seem to be any explicit documentation about our exact use-case.
Most docs describe classic password-saving scenarios and recommend bcrypt, scrypt or argon2.
These hashing functions are slow to stop brute-force or dictionary attacks, which would expose
the original, user-provided password, that may have been reused across multiple services.
We have a very different scenario:
Our API tokens are 64 bytes of cryptographically strong pseudorandom data.
Brute-force or dictionary attacks are therefore virtually impossible, and tokens are not
reused across multiple services.
We therefore need to only guard against one scenario:
An attacker gains read-only access to the database. Saving only hashes in the database prevents the
attacker from authenticating themselves as a user. The hash-function does not need to be very slow,
as the randomness of the original token prevents inverting the hash. The function actually needs to
be reasonably fast, as the hash must be computed on every request to the public API.
SHA-512 (or alternatively SHA3) fits this use-case.
## Private API
The private API uses a session cookie to authenticate the user.
Sessions are handled using [passport.js](https://www.passportjs.org/).
The backend hands out a new session token after the user has successfully authenticated
using one of the supported authentication methods:
- Username & Password (`local`)
- LDAP
- OIDC
The `SessionGuard`, which is added to each (appropriate) controller method of the private API,
checks if the provided session is still valid and provides the controller method
with the correct user.
[bearer-token]: https://datatracker.ietf.org/doc/html/rfc6750

View file

@ -0,0 +1,97 @@
# Config
!!! info "Design Document"
This is a design document, explaining the design and vision for a HedgeDoc 2
feature. It is not a user guide and may or may not be fully implemented.
The configuration of HedgeDoc 2 is handled entirely by environment variables.
Most of these variables are prefixed with `HD_` (for HedgeDoc).
NestJS - the framework we use - is reading the variables from the environment and also from
the `.env` file in the root of the project.
## How the config code works
The config of HedgeDoc is split up into **nine** different modules:
`app.config.ts`
: General configuration of the app
`auth.config.ts`
: Which authentication providers are available and which options are set
`csp.config.ts`
: Configuration for [Content Security Policy][csp]
`customization.config.ts`
: Config to customize the instance and set instance specific links
`database.config.ts`
: Which database should be used
`external-services.config.ts`
: Which external services are activated and where can they be called
`media.config.ts`
: Where media files are being stored
`note.config.ts`
: Configuration for notes
Each of those files (except `auth.config.ts` which is discussed later) consists of three parts:
1. An interface
2. A Joi schema
3. A default export
### Interface
The interface just describes which options the configuration has and how the rest of HedgeDoc can
use them. All enums that are used in here are put in their own files with the extension `.enum.ts`.
### Joi Schema
We use [Joi][joi] to validate each provided configuration to make sure the configuration of the user
is sound and provides helpful error messages otherwise.
The most important part here is that each value ends with `.label()`. This names the
environment variable that corresponds to each config option. It's very important that each config
option is assigned the correct label to have meaningful error messages that benefit the user.
Everything else about how Joi works and how you should write schemas can
be read in [their documentation][joi-doc].
### A default export
The default exports are used by NestJS to provide the values to the rest of the application.
We mostly do four things here:
1. Populate the config interface with environment variables, creating the config object.
2. Validate the config object against the Joi schema.
3. Polish the error messages from Joi and present them to the user (if any occur).
4. Return the validated config object.
## How `auth.config.ts` works
Because it's possible to configure some authentication providers multiple times
(e.g. multiple LDAPs or GitLabs), we use user defined environment variable names.
With the user defined names it's not possible to put the correct labels in the schema
or build the config objects as we do in every other file.
Therefore, we have two big extra steps in the default export:
1. To populate the config object we have some code at the top of the default export to gather all
configured variables into arrays.
2. The error messages are piped into the util method `replaceAuthErrorsWithEnvironmentVariables`.
This replaces the error messages of the form `gitlab[0].providerName`
with `HD_AUTH_GITLAB_<nameOfFirstGitlab>_PROVIDER_NAME`. For this the util function gets
the error, the name of the config option (e.g `'gitlab'`), the approriate prefix
(e.g. `'HD_AUTH_GITLAB_'`), and an array of the user defined names.
## Mocks
Some config files also have a `.mock.ts` file which defines the configuration for the e2e tests.
Those files just contain the default export and return the mock config object.
[csp]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
[joi]: https://joi.dev/
[joi-doc]: https://joi.dev/api

View file

@ -0,0 +1,18 @@
# Events
!!! info "Design Document"
This is a design document, explaining the design and vision for a HedgeDoc 2
feature. It is not a user guide and may or may not be fully implemented.
In HedgeDoc 2, we use an event system based on [EventEmitter2][eventemitter2].
It's used to reduce circular dependencies between different services and inform these services
about changes.
HedgeDoc's system is basically [the system NestJS offers][nestjs/eventemitter].
The config for the `EventEmitterModule` is stored in `events.ts` and
exported as `eventModuleConfig`. In the same file enums for the event keys are defined.
Each of these events is expected to be sent with an additional value.
In the enum definition a comment should tell you what exactly this value should be.
[eventemitter2]: https://github.com/EventEmitter2/EventEmitter2
[nestjs/eventemitter]: https://docs.nestjs.com/techniques/events

View file

@ -0,0 +1,45 @@
# Core concepts
Core concepts explain the internal structure of HedgeDoc by providing
background information and explanations. They are especially useful for contributing to HedgeDoc.
<!-- markdownlint-disable no-inline-html -->
<div class='topic-container'>
<a href='/development/concepts/notes/'>
<div class='topic'>
<span>📝</span>
<span>Notes</span>
</div>
</a>
<a href='/development/concepts/media/'>
<div class='topic'>
<span>📸</span>
<span>Media</span>
</div>
</a>
<a href='/development/concepts/user-profiles/'>
<div class='topic'>
<span>🙎</span>
<span>User Profiles</span>
</div>
</a>
<a href='/development/concepts/config/'>
<div class='topic'>
<span>🛠️</span>
<span>Config</span>
</div>
</a>
<a href='/development/concepts/api-auth/'>
<div class='topic'>
<span>🤖️</span>
<span>API Auth</span>
</div>
</a>
<a href='/development/concepts/events/'>
<div class='topic'>
<span>🎩</span>
<span>Events</span>
</div>
</a>
</div>
<!-- markdownlint-enable no-inline-html -->

View file

@ -0,0 +1,23 @@
# Media
!!! info "Design Document"
This is a design document, explaining the design and vision for a HedgeDoc 2
feature. It is not a user guide and may or may not be fully implemented.
Media is the term for uploads associated with a note in HedgeDoc.
Currently, there's only support for images.
Media files can be saved to different storage backends like the local filesystem, S3, Azure Blob
storage, generic WebDAV shares, or imgur.
Each storage backend needs to implement an interface with three methods:
- `saveFile(uuid, buffer, fileType)` should store a given file and may return stringified metadata
to store in the database for this upload. The metadata does not need to follow a specific format,
and will only be used inside the storage backend.
- `deleteFile(uuid, metadata)` should delete a file with the given UUID. The stored metadata can
be used for example to identify the file on the storage platform.
- `getFileUrl(uuid, metadata)` should return a URL to the file with the given UUID. The stored
metadata can be used to identify the file on the storage platform.
The returned URL may be temporary.
Uploads are checked for their MIME type and compared to an allow-list and if not matching rejected.

View file

@ -0,0 +1,109 @@
# Notes
!!! info "Design Document"
This is a design document, explaining the design and vision for a HedgeDoc 2
feature. It is not a user guide and may or may not be fully implemented.
Each note in HedgeDoc 2 contains the following information:
- publicId (`b604x5885k9k01bq7tsmawvnp0`)
<!-- markdownlint-disable proper-names -->
- a list of aliases (`[hedgedoc-2, hedgedoc-next]`)
<!-- markdownlint-enable proper-names -->
- groupPermissions
- userPermissions
- viewCount (`0`)
- owner
- revisions
- authorColors
- historyEntries
- description (`All you never wanted to know about notes`)
- title (`Notes`)
- tags (`[features, cool, update]`)
- version
The `publicId` is the default possibility of identifying a note. It will be a randomly generated
128-bit value encoded with [base32-encode][base32-encode] using the crockford variant and converted
to lowercase. This variant of base32 is used, because that results in ids that only use one case of
alpha-numeric characters and other url safe characters. We convert the id to lowercase, because we
want to minimize case confusion.
`aliases` are the other way of identifying a note. There can be any number of them, and the owner
of the note is able to add or remove them. All aliases are just strings (especially to accommodate
the old identifier from HedgeDoc 1 [see below](#conversion-of-hedgedoc-1-notes)), but new aliases
added with HedgeDoc 2 will only allow characters matching this regex: `[a-z0-9\-_]`. This is done to
once again prevent case confusion. One of the aliases can be set as the primary alias, which will be
used as the identifier for the history entry.
`groupPermissions` and `userPermissions` each hold a list of the appropriate permissions.
Each permission holds a reference to a note and a user/group and specify what the user/group
is allowed to do.
Each permission is additive, that means a user that has only the right to read a note via a group,
but the right to write via a different group or directly for his user, is able to write in the note.
The `viewCount` is a simple counter that holds how often the read-only view of the note in question
was requested.
`owner` is the user that created the note or later got ownership of the note. The current owner is
able to change the owner of the note to someone else. The owner of a note is the only person that
can perform the following actions:
- delete the note
- modify `aliases`
- remove all `revisions`
The `revisions` hold all revisions of the note. These are the changes to the note content and by
whom they were performed.
The `authorColors` each specify for the tuple user and note which color should be used
to highlight them.
The `historyEntries` hold the history entries this note is referenced in. They are mainly here
for the purpose of deleting the history entries on note deletion.
`description`, `tags` and `title` are each information specified in the [frontmatter][frontmatter]
of the note. They are extracted and saved in the database to allow the history page to show them and
do a search for tags without having to do a full-text search or having to parse the tags of
each note on search.
While `description` and `tags` are only specified by the [frontmatter][frontmatter], the title is
- the content of the *title* field of the [frontmatter][frontmatter] of the note
- **OR** the content of the *title* field in the *opengraph* field of the [frontmatter][frontmatter]
of the note
- **OR** the first level 1 heading of the note
which ever of these is the first to not be unspecified.
All mentioned fields are extracted from the note content by the backend on save or update.
`version` specifies if a note is an old HedgeDoc 1 note, or a new HedgeDoc 2 note.
This is mainly used to redirect old notes form <https://md.example.org/noteid>
to <https://md.example.org/n/noteid>.
## Deleting Notes
- The owner of a note may delete it.
- By default, this also removes all revisions and all files that were uploaded to that note.
- The owner may choose to skip deleting associated uploads, leaving them without a note.
- The frontend should show a list of all uploads that will be affected
and provide a method of skipping deletion.
- The owner of a note may delete all revisions. This effectively purges the edit
history of a note.
## Conversion of HedgeDoc 1 notes
First we want to define some terms of the HedgeDoc 1 notes:
- **noteId**: This refers to the auto-generated id for new notes.
(<https://demo.hedgedoc.org/Q_Iz5T_lQWGYxne0sbMtwg>)
- **shortId**: This refers to the auto-generated short id which is used for "published" notes and
slide presentation mode. (<https://demo.hedgedoc.org/s/61ZHI6HGE>)
- **alias**: This refers to user-defined URLs for notes on instances with Free-URL mode enabled.
(<https://md.kif.rocks/lowercase>)
The noteId, shortId and alias of each HedgeDoc 1 note are saved as HedgeDoc 2 aliases.
Each note gets a newly generated publicId.
[frontmatter]: https://jekyllrb.com/docs/front-matter/
[base32-encode]: https://www.npmjs.com/package/base32-encode

View file

@ -0,0 +1,99 @@
# User Profiles and Authentication
!!! info "Design Document"
This is a design document, explaining the design and vision for a HedgeDoc 2
feature. It is not a user guide and may or may not be fully implemented.
Each user in HedgeDoc 2 has a profile
which contains the following information:
- username (`janedoe`)
- display name (`Jane Doe`)
- email address, optional (`janedoe@example.com`)
- profile picture, optional
- the date the user was created
- the date the profile was last updated
HedgeDoc 2 supports multiple authentication methods per user.
These are called *identities* and each identity is backed by an
auth provider (like OIDC, LDAP or internal auth).
One of a users identities may be marked as *sync source*.
This identity is used to automatically update profile attributes like the
display name or profile picture on every login. If a sync source exists, the
user can not manually edit their profile information.
If an external provider was used to create the account,
it is automatically used as sync source.
The administrator may globally set one or more auth providers as sync source,
e.g. to enforce that all profile information comes from the corporate
LDAP and is the same across multiple applications.
If global sync sources exist, new accounts can only be created using
these auth providers. The auth provider that was used to create the account
is automatically set as sync source and cannot be changed by the user.
This effectively pins the account to this provider.
## Example: Corporate LDAP
The administrator wants to allow users to log in via the corporate LDAP
and Google. Login must only be possible for users present in LDAP and
all users must be displayed as they are in the LDAP.
The admin therefore sets up two login providers:
- corporate LDAP, marked as global sync source
- Google OAuth login
If a new user tries to log in via Google, they will not be found in the
database. The frontend detects that a global sync source exists and
suggests logging in via LDAP first.
After a new user created their account by logging in via LDAP, they can use
the 'add a new login method' feature in their profile page to link their
Google account and use it to login afterwards.
## Example: Username Conflict
HedgeDoc is configured with two auth providers.
- A user logs in using auth provider A.
- The backend receives the profile information from provider A and notices that the username
in the profile already exists in the database, but no identity for this provider-username
combination exists.
- The backend creates a new user with another username to solve the username conflict.
- The frontend warns the user that the username provided by the auth provider is already taken
and that another username has been generated. It also offers to instead link the new auth provider
(in this case A) with the existing auth provider (in this case B).
- If the user chooses the latter option, the frontend sends a request to delete the newly created
user to the backend.
- The user can then log in with auth provider B and link provider A using the "link auth provider"
feature in the profile page.
### Handling of sync sources and username conflicts
#### Global sync sources
If at the time of logging in with auth provider A, *only* A is configured as a *global* sync source,
the backend cannot automatically create a user with another username.
This is because:
- Creating new accounts is only possible with a sync source auth provider.
- Setting an auth provider as sync-source entails that profile information the auth provider
provides must be saved into the local HedgeDoc database.
- As the username the auth provider provides already exists in the database, a new user cannot
be created with that username.
In this case, the frontend should show the use a notice that they should contact an admin
to resolve the issue.
!!! warning
Admins must ensure that usernames are unique across all auth providers set as a global sync
source. Otherwise, if e.g. in both LDAPs configured as sync source a user `johndoe` exists,
only the first that logs in can use HedgeDoc.
#### Local sync sources
If auth provider A is configured as a sync source by the user, syncing is automatically disabled,
and a notice is shown. Re-enabling the sync source is not possible until the username conflict is
resolved, e.g. by changing the username in the auth provider.

View file

@ -0,0 +1,42 @@
# LDAP
If you are developing HedgeDoc and need to test something with an LDAP server you can use the
[`test-openldap`][docker-image] Docker image from [rroemhild][rroemhild].
Simply run
<!-- markdownlint-disable proper-names -->
```shell
docker run --rm -p 10389:10389 -p 10636:10636 rroemhild/test-openldap
```
<!-- markdownlint-enable proper-names -->
and add the following to the `.env` file then start the backend.
```dotenv
HD_AUTH_LDAP_SERVERS="FUTURAMA"
HD_AUTH_LDAP_FUTURAMA_PROVIDER_NAME="Futurama LDAP"
HD_AUTH_LDAP_FUTURAMA_URL="ldap://localhost:10389"
HD_AUTH_LDAP_FUTURAMA_SEARCH_BASE="ou=people,dc=planetexpress,dc=com"
HD_AUTH_LDAP_FUTURAMA_SEARCH_FILTER=(&(uid={{username}})(objectClass=inetOrgPerson))
HD_AUTH_LDAP_FUTURAMA_DISPLAY_NAME_FIELD="uid"
HD_AUTH_LDAP_FUTURAMA_USERID_FIELD="uid"
HD_AUTH_LDAP_FUTURAMA_BIND_DN="cn=admin,dc=planetexpress,dc=com"
HD_AUTH_LDAP_FUTURAMA_BIND_CREDENTIALS="GoodNewsEveryone"
```
You should be able to log in with either of these logins (`username` : `password`):
- `professor` : `professor`
- `fry` : `fry`
- `zoidberg` : `zoidberg`
- `hermes` : `hermes`
- `leela` : `leela`
- `bender` : `bender`
- `amy` : `amy`
If you need to know more about which information are held by each of these accounts, have a look at
the [documentation](https://github.com/rroemhild/docker-test-openldap#ldap-structure).
[docker-image]: https://github.com/rroemhild/docker-test-openldap
[rroemhild]: https://github.com/rroemhild

View file

@ -0,0 +1,22 @@
# Building Docker images
To build Docker images of the backend or frontend use the following commands.
Make sure that you have installed the [Docker BuildKit Plugin][buildkit] and
execute the commands from the root level of the project.
Otherwise, the build process may fail.
<!-- markdownlint-disable proper-names -->
```shell
docker buildx build -f backend/docker/Dockerfile -t hedgedoc-backend .
```
<!-- markdownlint-enable proper-names -->
or
<!-- markdownlint-disable proper-names -->
```shell
docker buildx build -f frontend/docker/Dockerfile -t hedgedoc-frontend .
```
<!-- markdownlint-enable proper-names -->
[buildkit]: https://docs.docker.com/build/install-buildx/

View file

@ -0,0 +1,44 @@
# Documentation
Our documentation is build with [mkdocs][mkdocs]. While you can write documentation with every text
editor you like, if you want to build the documentation and want to see at how it will look you need
to have [python3][python3] and [mkdocs][mkdocs] installed.
## Writing
<!-- markdownlint-disable proper-names -->
All documentation files are found in the `docs/content` directory of
the [hedgedoc/hedgedoc repo](https://github.com/hedgedoc/hedgedoc). These files are just normal
markdown files with nothing special about them.
<!-- markdownlint-enable proper-names -->
The configuration for mkdocs lies in the `docs` folder in a file called `mkdocs.yml`. With that file
the theme and menu - among others - can be configured.
**Please note:** Any new files need to be linked to by other files or put in the navigation,
otherwise the files will be very hard to find on the documentation website.
## Building
To build the documentation locally you need to perform the following steps:
1. Make sure you have [python3][python3] installed. `python3 --version`
2. Go into the `docs` folder.
3. Install all the dependencies (E.g. with a [venv](https://docs.python.org/3/library/venv.html))
with `pip install -r requirements.txt`
4. Start the mkdocs dev server (`mkdocs serve`) or build the documentation (`mkdocs build`).
If you run `mkdcs serve` a local server will serve the mkdocs files and change the served files as
you write documentation.
## Deployment
The documentation is deployed with [mkdocs][mkdocs].
<!-- markdownlint-disable proper-names -->
The repository [docs.hedgedoc.org][docs.hedgedoc.org] is used to deploy the actual website
to github.io. Currently only the `master` branch is deployed as it contains the latest release.
<!-- markdownlint-enable proper-names -->
[mkdocs]: https://www.mkdocs.org
[python3]: https://www.python.org/
[docs.hedgedoc.org]: https://github.com/hedgedoc/docs.hedgedoc.org

View file

@ -0,0 +1,62 @@
# Frontend
## Environment Variables
The following environment variables are recognized by the frontend process.
| Name | Possible Values | Description |
|--------------------------|----------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| HD_BASE_URL | Any URL with protocol, domain and optionally directory and port. Must end with a trailing slash. (e.g. `http://localhost:3001/`) | The URL under which the frontend is expected. Setting this is mandatory so the server side rendering can generate assets URLs. You only need to set this yourself if you use the production mode. |
| HD_RENDERER_BASE_URL | Same as `HD_BASE_URL` | You can provide this variable if the renderer should use another domain than the editor. This is recommended for security reasons but not mandatory. This variable is optional and will fallback to `HD_BASE_URL` |
| NEXT_PUBLIC_USE_MOCK_API | `true`, `false` | Will activate the mocked backend |
| NEXT_PUBLIC_TEST_MODE | `true`, `false` | Will activate additional HTML attributes that are used to identify elements for test suits. |
Variables that start with `NEXT_PUBLIC_` will be compiled into the build. You can't change them
after compilation. You shouldn't need to set them yourself. Use the designated npm tasks instead.
## UI Test
Curious about the new look and feel? We provide a demo of the new UI on
[HedgeDoc.dev][hedgedoc-dev]. This version is reset every day, so data is not persisted.
Please see also our [privacy policy][privacy].
## Running Tests
### Unit
Unit testing is done via jest.
1. Run `yarn test`
### End2End
We use [cypress][cypress] for e2e tests.
1. Start the frontend with `yarn start:dev:test` (or use a test build using `yarn build:test`
which you can start using `yarn start`). The usage of `:test` is mandatory!
2. Run `yarn test:e2e:open` to open the cypress test loader
3. Choose your browser and start a test suite
To run all tests in a headless browser use `yarn test:e2e`
### Bundle analysis
You can inspect the generated production-bundle files to look for optimization issues.
1. run `yarn analyze`. This will overwrite any existing builds!
2. Open the generated `.next/server/analyze/server.html` in your favourite browser
## Enable Debug Logging in Production
The debug logger can be enabled in production by setting `debugLogging` in the browser's
local storage to `true`. This can be done e.g. by executing this JavaScript command
in the browser's console.
```javascript
window.localStorage.setItem("debugLogging", "true");
```
[hedgedoc-dev]: https://hedgedoc.dev
[privacy]: https://hedgedoc.org/privacy-policy
[cypress]: https://cypress.io

View file

@ -0,0 +1,167 @@
# Development Setup
To run HedgeDoc 2.0 you need three components: the backend, the frontend and the reverse proxy.
Backend and Frontend are included in the [HedgeDoc repo][hedgedoc-repo].
The reverse proxy can be chosen by preference. For development, we recommend caddy
and the provided configuration.
## Quick guide for development setup
This describes the easiest way to start a local development environment. For other deployments
follow the description below.
To run HedgeDoc 2.0 you need three components: the backend, the frontend and the reverse proxy.
Backend and Frontend are included in the [HegdeDoc repo][hedgedoc-repo].
The reverse proxy can be chosen by preference. For development, we recommend caddy
and the provided configuration.
1. Clone [our repository][hedgedoc-repo] and go into its directory
<!-- markdownlint-disable proper-names -->
```shell
git clone https://github.com/hedgedoc/hedgedoc.git
cd hedgedoc
```
<!-- markdownlint-enable proper-names -->
2. Install Node.js. You need at least Node 20.
3. Install [Yarn][yarn]
4. Install Caddy (select one of the two options)
- [Download][caddy] and place the `caddy` binary in `dev-reverse-proxy`.
Ensure it is executable with `chmod +x caddy`. Users of macOS may need to run
`xattr -d com.apple.quarantine ./caddy` to lift the quarantine for executables
from the internet.
- Install Caddy using your package manager
5. Install the dependencies in repo root directory with `yarn install`
6. Create the `.env` config file by copying the example: `cp .env.example .env`
7. Run `yarn start:dev`
> This will execute the backend, frontend and reverse proxy at once
8. Use your browser to go to <http://localhost:8080>. This may take a while because everything is
compiled on the fly.
## More detailed development setup
The following sections describe a more detailed setup of all components.
## Preconditions
If you want to run HedgeDoc in dev mode some preconditions have to be met.
1. Make sure that Node.js is installed. You need at least Node 20.
2. Make sure that [Yarn][yarn] is installed.
<!-- markdownlint-disable proper-names -->
3. Clone this repo (e.g. `git clone https://github.com/hedgedoc/hedgedoc.git hedgedoc`)
<!-- markdownlint-enable proper-names -->
4. Go into the cloned directory
## Installing the dependencies
Because we use Yarn workspaces, Yarn collects the dependencies of all packages automatically in one
central top-level `node_modules` folder.
To install the dependencies execute `yarn install` at the top level of the cloned repository.
Execute this command ONLY there. There is no need to execute the install-command for every package.
It's important to use [Yarn][yarn]. We don't support `npm` or any other package
manager and using anything else than Yarn won't work.
## Create the configuration
HedgeDoc 2 is configured using environment variables.
For development, we recommend creating an `.env` file.
1. Create an `.env` file. We recommend to use the example file by running `cp .env.example .env`
You can modify this file according to the [configuration documentation][config-docs].
2. Make sure that you've set `HD_SESSION_SECRET` in your `.env` file. Otherwise, the backend
won't start.
> In dev mode you don't need a secure secret. So use any value. If you want to generate a secure
> session secret you can use
> e.g. `openssl rand -hex 16 | sed -E 's/(.*)/HD_SESSION_SECRET=\1/' >> .env`.
3. Make sure that `HD_BASE_URL` in `.env` is set to the base url where HedgeDoc should be available.
In local dev environment this is most likely `http://localhost:8080`.
## Build the `commons` package
Some code is shared by backend and frontend. This code lives in the `commons` package and needs
to be built so frontend and backend can import it.
This only needs to be done once, except if you've changed code in the commons package.
1. Go into the `commons` directory.
2. Execute `yarn build` to build the commons package.
## Setting up the Backend
**Note:** The backend can be mocked instead of starting it for real. This is useful,
if you just want to work on the frontend. See the "Mocked backend" section below.
1. Go into the `backend` directory.
2. Start the backend by running `yarn start:dev` for dev mode or `yarn start` for production.
## Setting up the frontend
The frontend can be run in four different ways. The development mode compiles everything on demand.
So the first time you open a page in the browser it may take some time.
[See here][frontend-setup] for a more detailed description of the environment variables
for the frontend. A special configuration isn't necessary but keep in mind that you execute
all commands from within the `frontend` directory.
### Mocked backend
This task will run the frontend in mock-mode, meaning instead of running a real backend, the
frontend mocks the backend. This way you can work on frontend functionality without starting up the
full development environment. The downside of this method is that you can't save notes and that
realtime collaboration features are not available. To start the development mode,
run `yarn start:dev:mock`. The app should run now and be available under
<http://localhost:3001> in your browser.
### With local backend
To start the development mode with an actual HedgeDoc backend use `yarn start:dev` instead.
This task will automatically set `HD_BASE_URL` to `http://localhost:8080`.
### Production mode
Use `yarn build` to build the app in production mode and save it into the `.next` folder.
The production build is minimized and optimized for best performance. Don't edit the generated
files in the `.next` folder in any way!
You can run the production build using the built-in server with `yarn start`.
You MUST provide the environment variable `HD_BASE_URL` with protocol, domain and (if needed)
subdirectory path (e.g. `http://localhost:3001/`) so the app knows under which URL the frontend
is available in the browser.
If you use the production build then make sure that you set the environment variable `HD_BASE_URL`
to the same value as `HD_BASE_URL` in the backend.
### Production mock build
It is also possible to create a production build that uses the emulated backend by using
`yarn build:mock`. This is usually not needed except for demonstration purposes like
`https://hedgedoc.dev`.
## Running backend and frontend together
To use backend and frontend together in development mode you'll need a local reverse proxy that
combines both services under one URL origin.
We recommend to use our pre-configured [Caddy][caddy] configuration.
### Running the reverse proxy
1. Download the latest version of Caddy from [the Caddy website][caddy] or alternatively install
it using your package manager. You don't need any plugin. Place the downloaded binary in
the directory `dev-reverse-proxy`. Don't forget to mark the file as executable using
`chmod +x caddy`. Users of macOS may need to run `xattr -d com.apple.quarantine ./caddy`
to lift the quarantine for executables from the internet.
2. Start Caddy using `./caddy run` (if you downloaded the binary manually) or `caddy run`
(if you installed Caddy via a package manager).
3. Open your browser on <http://localhost:8080>
It is also possible to use another domain and port other than `localhost:8080`.
To do so, you need to set the `HD_BASE_URL` environment variable accordingly.
Furthermore, for Caddy to work with a domain name (possibly creating TLS certificates),
set `CADDY_HOST` to your domain (for example `CADDY_HOST=http://my-hedgedoc.home:9000`).
[hedgedoc-repo]: https://github.com/hedgedoc/hedgedoc
[yarn]: https://yarnpkg.com/getting-started/install
[caddy]: https://caddyserver.com/
[config-docs]: ../../config/index.md
[frontend-setup]: ./frontend.md