fix: docs build (#1203)

This commit is contained in:
Mo
2022-07-04 15:42:47 -05:00
committed by GitHub
parent f11b8e163c
commit 66d5dce5e1
267 changed files with 43 additions and 41 deletions

View File

@@ -0,0 +1,23 @@
---
title: How to contribute
sidebar: How to contribute
description: How to contribute to Standard Notes
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- Template
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
Thank you for your interest in contributing to Standard Notes. We are very happy that you want to help. The development for all our apps are done publicly on [GitHub](https://github.com/standardnotes). Here are some ways that you can help us improve Standard Notes:
1. Submit suggestions and feature ideas to our [community forum](https://forum.standardnotes.org). You can comment on the existing feature requests and subscribe to individual threads to follow their progress.
2. Report bugs or issues to our [community forum](https://forum.standardnotes.org).
3. Help others on the forum and our [Slack group](https://standardnotes.com/slack).
4. Blog, tweet and share what you like about Standard Notes, what you use it for, and how it helps you.
We do things the old-fashioned way and rely on people like you telling other people like you about Standard Notes to sustain our efforts. Get a free month of Extended when your friend also joins Extended. Log into the [member dashboard](https://dashboard.standardnotes.com) to get your unique referral link. 🙂

View File

@@ -0,0 +1,191 @@
---
id: actions
title: Actions
sidebar_label: Actions
description: How to build Action components for Standard Notes.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- build an extension
- actions
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
Actions are menu-based extensions that allow you to build simple APIs that do not require a user interface. Actions have the power to receive the working note and modify it. We use actions for our Note History extension, as well as Listed and File Attachments.
## Building an Action
Building an action-based extension can be done through any backend system of your choosing. We use Rails to build the [Listed](https://github.com/standardnotes/listed) extension, which allows you to create and manage a blogging publication from your notes.
In this example, we'll recreate a simple clone of Listed.
1. Generate a secret installation link for the user.
The secret installation link will contain a randomly generated secret key that authenticates the user to the server. The user need only copy the resulting link into Standard Notes, and it is then remembered automatically and sent to the server with every subsequent request.
```ruby
author = Author.new
secret = Digest::SHA256.hexdigest(SecureRandom.hex)
secret_url = CGI.escape("#{ENV['HOST']}/authors/#{author.id}/extension/?secret=#{secret}&type=sn")
```
Display the `secret_url` to the user and instruct them to install the url in Standard Notes, via the Extensions menu in the lower left corner.
1. Whenever the user clicks on the "Actions" menu within Standard Notes, SN will make a GET request to the user's secret URL. It is here that you return a JSON object that contains some metadata and applicable actions.
Here is what Listed handles the `extension` endpoint that is encoded in the user's secret url:
```ruby
def extension
secret = params[:secret]
item_uuid = params[:item_uuid]
name = "Listed"
supported_types = ["Note"]
actions = []
if item_uuid
actions += [
{
:label => "Publish to Blog",
:url => "#{ENV['HOST']}/authors/#{@author.id}/posts/?unlisted=false&secret=#{secret}&item_uuid=#{item_uuid}",
:verb => "post",
:context => "Item",
:content_types => ["Note"],
:access_type => "decrypted"
},
{
:label => "Open Blog",
:url => @author.username && @author.username.length > 0 ? @author.url : "#{ENV['HOST']}/authors/#{@author.id}",
:verb => "show",
:context => "Item",
:content_types => ["Note"],
:access_type => "decrypted"
},
{
:label => "Publish to Private Link",
:url => "#{ENV['HOST']}/authors/#{@author.id}/posts/?unlisted=true&secret=#{secret}&item_uuid=#{item_uuid}",
:verb => "post",
:context => "Item",
:content_types => ["Note"],
:access_type => "decrypted"
}
]
end
post = Post.find_by_item_uuid(item_uuid)
if post
actions.push(
{
:label => "Open Private Link",
:url => "#{ENV['HOST']}/#{post.token}",
:verb => "show",
:context => "Item",
:content_types => ["Note"]
})
actions.push(
{
:label => "Unpublish",
:url => "#{ENV['HOST']}/authors/#{@author.id}/posts/#{post.id}/unpublish?secret=#{secret}",
:verb => "post",
:context => "Item",
:content_types => ["Note"]
})
end
actions.push (
{
:label => "Settings",
:url => "#{ENV['HOST']}/authors/#{@author.id}/settings?secret=#{secret}",
:verb => "show",
:context => "Item",
:content_types => ["Note"]
}
)
description = "Publishes to listed.to. Requires decrypted access to publishing note."
render :json => {
:name => name,
:description => description,
:supported_types => supported_types,
:actions => actions,
:content_type => "Extension",
:identifier => "com.my.extension"
}
end
```
1. When a user selects the action, your server should be ready to handle that endpoint, and in most cases expect an item. Here's how Listed handles the "Publish to Blog" action:
```ruby
def create
item_uuid = params[:item_uuid]
post = Post.find_by_item_uuid(item_uuid)
if post && post.author != @author
return
end
if !post
post = @author.posts.new(post_params)
else
post.update(post_params)
end
item = params[:items][0]
content = item["content"]
post.title = content["title"]
post.text = content["text"]
post.save
end
```
### Properties
Actions have the following properties:
| Key | Description |
| :------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`label`** | What the UI will display for this action. |
| **`url`** | The URL that Standard Notes will make a request to when the user selects this action. |
| **`verb`** | Instructs Standard Notes how to handle the URL. This can be one of: |
| **`show`** | Standard Notes will open the `url` in a browser. |
| **`post`** | Standard Notes will make a POST request to the `url` with the current item included in the parameters. |
| **`get`** | Standard Notes will make a GET request to the `url` and expect an `Item` in response. The item will be used to update the current working note. We use this for our Note History extension to update the current note with a previous version of it. |
| **`render`** | Standard Notes will make a `GET` request to the `url` and expect an `Item`, but instead of updating the item, it will preview it in a modal. This allows a user to preview the contents of an incoming item before choosing to replace the current note with whatever is retrieved from the server. We also use this in our Note History extension. |
| **`context`** | Context should mostly be `Item`, which means that this action applies to a particular item, and is not just a general action. In the past, `context` could take on the value of `global`, which means it has actions available that are not related to an item. However, this functionality is unofficially deprecated, with an official deprecation coming soon. |
| **`content_types`** | The kinds of items this action applies to. Currently only 'Note' actions are supported. In the future, we might allow for actions on a `Tag` or other content types, but no such interface is currently available. |
For example, the expected response of a **`get`** action is:
```json
{
"item": {
"uuid": "",
"content_type": "",
"content": "",
"created_at": "",
"updated_at": ""
}
}
```
The payload inside the `item` key is the same payload structure you would see if you downloaded an encrypted backup file from the Account menu and inspected the `.txt` file. The item needs to be in the encrypted format when it appears. We'll need to modify the client code to also accept decrypted items.
## Installing an Action
Actions have the following URL format:
```
https://host.org/my-action?type=action&name=MyAction
```
## Action Potential
There are a lot of cool things you can build with actions. For example, you can build an action that receives the current note which consists of a bunch of numbers separated by a comma, and the action can compute the average, and return the new note contents which appends the average. This is a simple use case, but can be enlarged to build more powerful abilities.
You might even build an action that for example receives JavaScript code in the note text, runs the JavaScript, computes the result, and returns the result which is then appended to the note body in creative ways. The possibilities are almost endless.

View File

@@ -0,0 +1,143 @@
---
id: building-an-extension
title: Building an Extension
sidebar_label: Building an Extension
description: How to build an extension for Standard Notes.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- build an extension
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
In this section, we'll talk about building **components**, which are a type of extension related to user interface elements that can replace or be appended to areas of the Standard Notes app. They allow us to do cool things like custom editors, nested folders, tag autocomplete, and custom extension bars in the editor pane.
Building a component is easily done using the JavaScript-based [Component Bridge library](https://github.com/sn-extensions/components-api). All you have to do is build a single-page web app using any framework you'd like (plain, Angular, React, etc.), then use our component "bridge" to handle interactions between Standard Notes and your extension, for example, to read or save data.
## Setting up the project
In this example, we'll use our blank-slate ReactJS template to build a utility bar that counts and displays the number of words in the current note.
(The ReactJS template makes it easy to get started. You can also create a project from scratch that utilizes the [Components Bridge library](https://github.com/sn-extensions/components-api).)
1. Clone the [blank-slate](https://github.com/sn-extensions/react-blank-slate) project from GitHub:
```bash
git clone https://github.com/sn-extensions/react-blank-slate.git
```
1. Build the project:
```bash
cd react-blank-slate
npm install
```
1. Start the local web server to host the app.
```bash
npm run start
```
1. In the command output from above, note the port number used. By default, it will probably be port 8080 if it's available. Open `localhost:8080` in your browser. You should see the text "Component is ready" on the page.
## Installing in Standard Notes
1. In the `app` folder, you will find a file called `ext.json`. This file instructs Standard Notes on how to install your extension. After having run the last step from the previous section (`npm run start`), you should have a localhost endpoint running.
1. In your browser, open `http://localhost:8080/ext.json`, and ensure that the output matches the file contents of the ext.json file. Most importantly, if your dev server is running on a different port, make sure to update the `url` property of the JSON file to reflect the correct value.
1. In the **Standard Notes** desktop application (browser may not work for this), click **Extensions** in the lower left corner of the app, click **Import Extension** in the bottom right of the **Extensions** window, and input your ext.json location: `http://localhost:8080/ext.json`. Then press enter.
1. In the same window, find your installed extension, then press **Activate** to run it.
1. You should now see "Blank Slate" at the bottom left of your notes. Clicking on it should raise a little window which displays "Your component is ready". If you try in your browser, it is possible that the window instead remains white. This is most likely due to your browser blocking Mixed Content page. Search online for how to enable it for your browser.
More detailed instructions on setting up your local environment can be found in the [Local Setup tutorial](/extensions/local-setup).
## Writing the App
1. In order to count the number of words in a note, the component needs access to the "working note", or the note the user is currently editing. In `app/lib/BridgeManager.js`, uncomment the relevant parts of the permissions so it looks like this:
```javascript
var permissions = [
{
name: 'stream-context-item',
},
];
```
1. Uncomment the function `streamContextItem` so it looks like this:
```javascript
this.componentManager.streamContextItem((item) => {
this.note = item;
this.notifyObserversOfUpdate();
});
```
Whenever a change is made to the working note, the block in that function will be called automatically.
1. In `app/components/Home.js`, create a function called `analyzeNote` that will count the number of words in the note's text:
```javascript
analyzeNote() {
let wordCount = this.state.note.content.text.match(/\b/gm).length / 2;
this.setState({wordCount: wordCount});
}
```
1. In the constructor of the Home class, call `analyzeNote` in the BridgeManager updateObserver so that it looks like this:
```javascript
BridgeManager.get().addUpdateObserver(() => {
this.setState({ note: BridgeManager.get().getNote() });
this.analyzeNote();
});
```
1. In the `render` function, add the following inside the first `div` of the `{this.state.note}` conditional:
```html
<p>Number of words: <strong>{this.state.wordCount}</strong></p>
```
Save all changes, then reload the entire Standard Notes web page. You should now see your word count update live as you type.
**Important:** The dev server auto-reloads the extension window inside Standard Notes, and by doing so, destroys the bridge connection between Standard Notes and the extension. Whenever you make a change, it's best to reload the entire Standard Notes window via Ctrl/Cmd + R in either the web or desktop app.
---
If you'd like to see the finished product, switch to the `word-count` branch:
```bash
git checkout word-count
```
## Available Areas
Areas tell Standard Notes where to display a particular component. The current list of available areas are:
| Key | Description |
| :-------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tags-list` | replaces the tags pane with a custom component. We use this for the Folders component. |
| `note-tags` | replaces the editor pane's tags area with a custom component. We use this for autocomplete tags. |
| `editor-stack` | adds custom-sized components in a stack in the editor pane. This does not replace any native modules but simply adds layers on top of the editor pane. We use this for the Action Bar and GitHub Push components. |
| `editor-editor` | replaces the plain text editor with a custom editor. We use this for all of our editors, including Markdown, Code, and Plus. |
| `themes` | replaces the default css styles with a custom set of styles. |
![Image of areas](/img/extensions/areas.png)
## Next Steps
There are an unlimited number of things you can build with components that do anything from nested folders in the tags pane and autocomplete in the editor pane, to pushing notes to GitHub or WordPress.
To see how we built [our own components](https://standardnotes.com/extensions), study the source code [available here](https://github.com/sn-extensions).
For questions on development, [post in the forum](https://forum.standardnotes.org) or [join our Slack](https://standardnotes.com/slack).
If you'd like to support Standard Notes and use our secure hosting to install all the components we have to offer, consider purchasing the [Extended subscription](https://standardnotes.com/extended).

View File

@@ -0,0 +1,38 @@
---
title: How to build editors with EditorKit
sidebar_label: EditorKit
description: How to use the Standard Notes EditorKit to build an editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- build an extension
- EditorKit
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
The EditorKit is a wrapper for the Components API to make it easier to develop editors for Standard Notes.
## Examples
The documentation for the EditorKit is incomplete. You can view the following examples to see how it is used.
- [Token Vault](https://github.com/sn-extensions/token-vault)
## Development
The Standard Notes EditorKit is written in JavaScript and compiled with Webpack.
## License
The Standard Notes EditorKit is licensed under the GNU [AGPL-3.0-or-later](https://github.com/standardnotes/editor-kit/blob/master/LICENSE).
## Resources
- [GitHub](https://github.com/standardnotes/editor-kit)
- [NPM](https://www.npmjs.com/package/sn-editor-kit)

View File

@@ -0,0 +1,98 @@
---
slug: editors/getting-started
id: editors-getting-started
title: Getting Started with Building Editors
sidebar_label: Getting Started
description: Getting Started with Building Editors
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- Getting Started with Building Editors
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Overview
This page provides a list of links to the technologies and practices that we recommend using for building editors. These technologies are used in the [Editor Template - Create React App and TypeScript](https://github.com/standardnotes/editor-template-cra-typescript). If you are familiar with React, TypeScript, and Sass, then you can skip this overview and go straight to the [README.md of the Editor Template](https://github.com/standardnotes/editor-template-cra-typescript#readme).
### Platforms
- [GitHub](https://github.com/) - A website to store the source code of your editor and to host a usable copy of your editor
### Programming Languages
We recommend using the following programming languages to build editors:
- [HTML](https://developer.mozilla.org/en-US/docs/Web/HTML) - A programming language for conveying meaning
- [CSS](https://developer.mozilla.org/en-US/docs/Web/CSS) - A programming language for conveying style
- [SCSS](https://sass-lang.com/documentation/syntax) - A programming language that is like CSS but is easier to write
- [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript) - A programming language for performing tasks
- [TypeScript](https://www.typescriptlang.org/) - A programming language that is like JavaScript but is easier to check for errors
### Environment
To get started with building editors, we recommend downloading the following technologies on your desktop computer:
- [Node.js](https://nodejs.org/) - An environment where you can run JavaScript code to run
- NPM or [Yarn](https://yarnpkg.com/) - A [package manager](https://wikipedia.org/wiki/Package_manager) for the JavaScript programming language. NPM comes with Node.js, but we use Yarn
- [Visual Studio Code](https://code.visualstudio.com/) - A text editor to edit the source files of your editor
- [Git Bash](https://git-scm.com/downloads) - A tool to interact with your file system
### Packages and Libraries
We recommend using the following packages and libraries:
- [Prettier](https://prettier.io/docs/en/index.html) - A package for formatting your code
- [ESLint](https://eslint.org/docs/user-guide/getting-started) - A package for checking your JavaScript and TypeScript code for errors
- [React](https://reactjs.org/docs/getting-started.html) - A library for building web applications using JavaScript
- [Create React App](https://create-react-app.dev/) - A package that makes it easy to get started with React
### Practices
We recommend that you follow these practices:
- [Conventional Commits](https://www.conventionalcommits.org/) - A specification for adding human and machine readable meaning to commit messages
## Files
The [Editor Template - Create React App and TypeScript](https://github.com/standardnotes/editor-template-cra-typescript) has the following files:
```none
editor-template-cra-typescript
├── .gitignore
├── .prettierrc
├── LICENSE
├── README.md
├── package.json
├── tsconfig.json
├── yarn.lock
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ ├── package.json
│ ├── robots.txt
│ └── sample.ext.json
└── src
├── Editor.test.tsx
├── index.scss
├── index.tsx
├── logo.svg
├── react-app-env.d.ts
├── reportWebVitals.ts
├── setupTests.ts
├── components
│ └── Editor.tsx
└── stylesheets
├── dark.scss
├── main.scss
└── print.scss
```
When you are familiar with these technologies, follow the instructions in the [README.md of the Editor Template](https://github.com/standardnotes/editor-template-cra-typescript#readme).

View File

@@ -0,0 +1,56 @@
---
slug: editors
id: editors
title: Building Editors for Standard Notes
sidebar_label: Building Editors
description: Building Editors for Standard Notes
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- Building Editors for Standard Notes
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
Editors are front-end extensions for Standard Notes. They provider users with a custom way to interact with the contents of their notes. For more information on editors in the Standard Notes ecosystem from the user's perspective, please see ["What are Editors?"](https://standardnotes.com/help/77/what-are-editors). In this article, we provide motivation and information about building editors.
## Why build editors
The process of building editors is like this: research, design, develop, document, publish/release, market, and repeat. Here are four big reasons to go through the process of building your own editor:
1. **Building editors is fun.** When you build editors, you can practice your creativity and imagination. Editors are like works of art that you can use to help you accomplish a goal, and creating works of art can be fun at any skill level.
2. **Building editors can help other people.** When you build editors to solve a problem that you have, there are probably other people who have similar problems. You can share your editor with them, help them solve their problems, and enjoy the feeling of knowing that you are helping others.
3. **Building editors gives you control over their features.** When you build editors, you can choose which features to implement and how they work, and you can implement them at your own pace. The engineering team at Standard Notes tends to focus on improving the core products, services, and infrastructure. If you have an idea or feature that you want to implement at the editor level of the ecosystem, then you can pursue it and have full control over it. You can have control over the future of your relationship with private productivity.
4. **Building editors is a great learning experience.** When you build editors, you can learn more about yourself and what you like to do. The entire process of building editors improves your fluency in skills that are important for a variety of career opportunities in the technology industry, including software development, software engineering, product design, product management, and technical writing. Building editors is a great way to gain more experience and to get involved in the technology industry.
The engineering team at Standard Notes works to solve many difficult problems so you, a developer of editors, do not have to worry about them. These problems include how to sync the contents of the notes between mobile and desktop devices with end-to-end encryption, how to make the sync work instantaneously, and how to make the notes secure on each device. They work to create a way for editors to communicate with the main notes application and make it easy for that communication to work offline.
Free tools such as GitHub provide a place for you to store the source code of your editor and deploy a version of it to a highly available, global content delivery network without having to worry about server management.
With Standard Notes and GitHub, you can create very sophisticated single page applications without having to worry about problems in back-end engineering. The Standard Notes community has many members who are ready and available to experience new innovations in note-taking and private productivity. When building editors in Standard Notes, you do not have to worry about not having a market for your creations. You can focus on creating and be confident that we will be excited to see what you build.
## Licensing
Standard Notes provides you with a wealth of opportunities to get started with front-end software engineering. The editors communicate with the Standard Notes application through an open-source library called the [Component Relay](https://github.com/standardnotes/component-relay).
The Component Relay is released under the [Affero General Public License Version 3.0](https://github.com/standardnotes/component-relay/blob/main/LICENSE) or any later version (AGPL-3.0-or-later). This a summary of the license provided by GitHub:
> Permissions of this strongest copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights. When a modified version is used to provide a service over a network, the complete source code of the modified version must be made available.
In other words, if you build an editor with the Component Relay and give copies of it to other people or let them use it, then you are legally obligated to provide them with the source code of the editor under the same license (AGPL-3.0-or-later). A common and easy way to do this is to host the source code in a public repository on GitHub.
We use AGPL-3.0-or-later to protect the community. Without the license, large technology companies (think "big tech") and startups backed by venture capital could potentially copy Standard Notes technology, rebrand it, and use the rebranded copy to drive Standard Notes out of business, thereby eliminating the future of Standard Notes as a commercially viable product and service. The Standard Notes community depends on Standard Notes being free, open-source, and trustworthy for many years to come, and the community is important and essential to us, so we use the AGPL-3.0 license. You are free to solicit fees and donations when marketing your editor, but we ask that you respect the license to help us protect the community.
## Standard Notes as an operating system
Operating systems such as MacOS, Windows, and Linux allow users to build their own desktop apps and run them on their computer as they please. Similarly, Standard Notes allows developers to build their own editors and use them in their Standard Notes app as they please. Therefore, you can think of Standard Notes as an operating system. The possibilities for building editors (apps) for the Standard Notes operating system are almost endless. Some possibilities to get you started include editors for code, markdown, rich text, passwords, bookmarks, calendars, charts, grids, kanban boards, presentation slides, spreadsheets, timelines, to-do lists, two-factor authentication codes, and other structured secrets. Think of a problem that you want to solve, and try building an editor to solve it.
[Get Started with Building Editors →](./editors-getting-started.md)

View File

@@ -0,0 +1,44 @@
---
slug: intro
id: intro
title: Intro to Extensions
sidebar_label: Intro to Extensions
description: Documentation for the Standard Notes Extensions.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- build an extension
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Philosophy
Standard Notes is built on the core principle of simplicity, in hopes of optimizing for longevity. This requires a different sort of architecture and long-term thinking. In today's environment, adding new features can be as easy as installing an external library and hooking it up with just a few lines of code. It's all too common that developers, in hopes of attracting more and more users, add more and more features to their app, until inevitably, the app bloats to oblivion and becomes impossible to maintain.
Rather than tightly couple every feature we dream of building for Standard Notes into the core application code, we chose to design Standard Notes to be thoroughly extensible. This allows us to experiment with new features without polluting the core application and threatening its stability and survivability.
## Sustainability
The Extensions model is also our main fundraising method, and allows us to to offer our core privacy experience at no cost, while sustaining future development by offering advanced features through our [Extended](https://standardnotes.com/extended) program.
Most of our extensions are [open-source](https://github.com/sn-extensions) and available for self-hosting. You can also learn to develop your own extensions by following the guides on this site. However, we encourage you to support the sustainability and future development of this project by [purchasing a subscription](https://standardnotes.com/extensions).
## Types
There are 3 main types of extensions:
1. **Components**: Components are user interface elements that completely swap out areas of Standard Notes with custom behavior. Components include editors (such as the [Markdown Pro Editor](https://standardnotes.com/extensions/markdown-pro) and [Plus Editor](https://standardnotes.com/extensions/plus-editor)), editor stack components (like the [Action Bar](https://standardnotes.com/extensions/action-bar) and [FileSafe](https://standardnotes.com/extensions/filesafe)), and other components (like [Folders](https://standardnotes.com/extensions/folders) and [Quick Tags](https://standardnotes.com/extensions/quick-tags)).
**[Develop a component →](/extensions/building-an-extension)**
2. **Themes**: Themes change the visual appearance of Standard Notes, and are compatible on all platforms, including desktop, web, and mobile. Some of our own themes include [Midnight](https://standardnotes.com/extensions/midnight) and [Solarized Dark](https://standardnotes.com/extensions/solarized-dark).
**[Develop a theme →](/extensions/themes)**
3. **Actions**: Actions are an API-based extension interface that are accessible via the Actions menu in the note pane. Actions are triggered manually by the user by selecting an action from the list, and have the ability to interface with the current note and send or retrieve content from a remote server. We use actions for places such as [Listed](https://listed.to), which is our blogging platform for Standard Notes users.
**[Develop an action →](/extensions/actions)**

View File

@@ -0,0 +1,85 @@
---
id: local-setup
title: Local Setup
sidebar_label: Local Setup
description: How to set up a development environment to build a Standard Notes extension.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- build an extension
- local setup
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
These instructions describe how to run an extension in a local environment.
Installing an extension inside Standard Notes consists of two main components:
- A JSON file that describes the extension, and includes metadata such as the extension's current version, description, hosted URL, and download URL.
- The source code for the extension.
To get your extension running locally, both of these components must be hosted on a local web server. In this guide, we'll use the command line server `http-server`.
## Steps
1. Install http-server:
```bash
npm install -g http-server
```
1. In your extension's root directory, run the following command to begin hosting your local server:
```bash
http-server -p 8001 --cors
```
The `--cors` option allows the Standard Notes app to load your extension via cross-origin resource sharing (required).
1. In your extension's root directory, create a file called `ext.json`.
1. Place, at minimum, the following key/value pairs. For the full listing of keys, see the [Publishing guide](/extensions/publishing).
```json
{
"identifier": "org.yourdomain.my-extension",
"name": "My Extension",
"content_type": "SN|Component",
"area": "editor-editor",
"version": "1.0.0",
"url": "http://localhost:8001"
}
```
The `url` should point to where your extension's index.html is hosted on your local server.
The `area` describes what kind of extension this will be. A list of valid values can be found in the [Publishing guide](/extensions/publishing).
1. In your browser, open http://localhost:8001/ext.json and make sure you see the JSON file content from above.
1. Copy the `url` from the JSON content and open it in your browser. Here, you should see your actual extension running. Your server will look for an `index.html` file by default.
If your main HTML file is called something different, or is not located in the root directory, simply change the `url` value in the JSON file to reflect this location. For example:
```bash
url: "http://localhost:8001/dist/index.html"
```
1. At this point, your extension is ready to be installed. Open **Standard Notes**, and click on **Extensions** in the lower left corner of the app.
1. In the bottom right of the **Extensions** window, click **Import Extension**. In the Extension Link field, enter the URL of your ext.json file. In this case, it will be `http://localhost:8001/ext.json`. Then press enter.
1. You should see a message that your extension was successfully installed. You can now scroll up in the **Extensions** window, and click **Activate** next to your extension to run it. If it is an editor, Editors can be activated via the **Editor menu** in the note panel, under the note title.
---
### Themes
If you're creating a theme, you would follow the same instructions, and for `area` in the JSON file, use "themes", and for the URL, it should link directly to your css file, i.e `http://localhost:8001/theme.css`.
### Publishing
Once you're ready to ship your extension in a production environment, check out the [Publishing guide](/extensions/publishing) to learn more about configuring your extension to autoupdate and be installed in an offline environment.

View File

@@ -0,0 +1,101 @@
---
id: publishing
title: Publishing
sidebar_label: Publishing
description: How to publish an extension for Standard Notes.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- build an extension
- publish an extension
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
Once you've crafted the code for your extension, you'll need to create a JSON description of your package, so that it can be imported using the Extension manager.
The JSON description must be hosted on a URL the app can reach out to. In these steps, we'll use Listed to generate a JSON endpoint.
## Steps:
1. Create a new note in Standard Notes called "my-extension.json", and enter the following as its contents:
```json
---
metatype: json
---
{
"identifier": "org.yourdomain.my-markdown-editor",
"name": "My Markdown Editor",
"content_type": "SN|Component",
"area": "editor-editor",
"version": "1.0.0",
"description": "A fully featured Markdown editor that supports live preview, a styling toolbar, and split pane support.",
"url": "https://domain.org/link-to-hosted-extension",
"download_url": "https://github.com/sn-extensions/advanced-markdown-editor/archive/1.0.184.zip",
"latest_url": "https://listed.to/my-extension-json-link",
"marketing_url": "https://standardnotes.com/extensions/advanced-markdown",
"thumbnail_url": "https://domain.org/editors/adv-markdown.jpg"
}
```
1. Head over to [listed.to](https://listed.to), and generate an account by clicking "Generate Author Link". Copy that link, then, in the Extensions menu in Standard Notes, click "Import Extension" in the bottom right corner. Paste in your link, and press enter, then accept.
2. Back in your "my-extension.json" note, click "Actions" in the menu bar below the note title, then click "Publish to Private Link". Click "Open Private Link" to preview your JSON endpoint.
3. In the Extensions window, click "Import Extension", and paste in your JSON endpoint, press Enter, then accept to install your extension.
### Key Descriptions
| Key | Description |
| :------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| identifier | A unique, reverse domain style identifier for your extension. |
| name | The name of your extension. |
| content_type | Generally SN&#124;Component, or SN&#124;Theme if theme package. |
| area | One of \[editor-editor, editor-stack, themes, note-tags, tags-list\] |
| version | The current version of your extension. Needs to match the version in your GitHub repo's package.json file. |
| description | The description that will be displayed in the Extensions browser. |
| url | Used by the web and mobile app. This value is the location of the hosted extension. It must be a link to either an HTML file \(for components\) or a CSS file \(for themes\). |
| download_url | Used by the desktop app. Must be a zip file. Caters strongly to GitHub auto-generated release files. The zip file must contain a package.json file with at least a version key. By default, the desktop app will look for an "index.html" in the root directory of the zip file. \(Note that if using GitHub auto-generated zip files, the contents of the zip file are embedded within a folder. The desktop app automatically unnests the folder contents and moves it up one level so it is on the root level.\) If your main file is not called index.html, or if it is a CSS file, please see below in "Local Installation" under "To specify the root file of your extension" instructions. |
| marketing_url | If specified, the Extensions manager will display an "Info" button, which on click, will open a web browser to this location. |
| thumbnail_url | The image the Extensions manager will display for your extension. |
## Local Installation
Extensions in Standard Notes desktop support local installation. We recommend using GitHub releases to host your download file.
1. In your extension repository on GitHub, go to the Releases tab, then click "Draft New Release".
2. Click "Publish release" \(you don't need to enter any information\). Standard Notes will use the auto-generated "Source code \(zip\)" archive that GitHub generates.
3. Right click on "Source code \(zip\)", then choose the option to Copy Link Address. Use this value for `download_url` in your JSON contents.
By default, Standard Notes will look for the `index.html` file located in the root of the project. If your main file is not in the root, you can specify a custom path.
### To specify the root file of your extension:
1. In your extension's repository, create a file called `package.json` if it does not already exist.
2. Add the following entry anywhere in the root level of `package.json`:
```json
...
"sn": {
"main": "relative/path/to/index.html"
},
...
```
`main` can also be a CSS file if you're publishing a theme.
## Autoupdate
Standard Notes will ping the `latest_url`endpoint automatically to update extensions. For this value, you can use the same JSON endpoint you're using for `url` \(your `my-extension.json` endpoint\).
## Questions?
Join the [Slack group](https://standardnotes.com/slack) and ask away in the \#dev channel.

View File

@@ -0,0 +1,28 @@
---
title: How to use the Standard Notes StyleKit
sidebar_label: StyleKit
description: How to use the Standard Notes StyleKit when building an extension.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- build an extension
- StyleKit
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
Each of the Standard Notes themes use a common set of CSS variables. With the Standard Notes StyleKit, you can use those variables inside the editors.
You can find a list of variables [here](/extensions/themes).
## License
The Standard Notes StyleKit is licensed under the GNU [AGPL-3.0-or-later](https://github.com/sn-extensions/StyleKit/blob/master/LICENSE).
## Resources
- [GitHub](https://github.com/sn-extensions/StyleKit)
- [NPM](https://www.npmjs.com/package/sn-stylekit)

View File

@@ -0,0 +1,132 @@
---
id: themes
title: Themes
sidebar_label: Themes
description: How to build a theme for Standard Notes.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- build an extension
- theme
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
Themes allow you to customize the look and feel of the Standard Notes app on all platforms.
You can view the [source code](https://github.com/sn-extensions/solarized-dark-theme) of our official themes in order to best understand how to create your own theme.
For how to install a theme, please see [Publishing](/extensions/publishing).
## Creating a theme
Themes are simple CSS files which override a few variables to style the look of the application. **CSS themes will automatically work on mobile.** Your CSS file should contain content similar to the below.
_Note that font and font sizes do not apply to mobile; only desktop/web._
```css
:root {
--sn-stylekit-base-font-size: 14px;
--sn-stylekit-font-size-p: 1rem;
--sn-stylekit-font-size-editor: 1.21rem;
--sn-stylekit-font-size-h6: 0.8rem;
--sn-stylekit-font-size-h5: 0.9rem;
--sn-stylekit-font-size-h4: 1rem;
--sn-stylekit-font-size-h3: 1.1rem;
--sn-stylekit-font-size-h2: 1.2rem;
--sn-stylekit-font-size-h1: 1.3rem;
--sn-stylekit-neutral-color: #989898;
--sn-stylekit-neutral-contrast-color: white;
--sn-stylekit-info-color: #086dd6;
--sn-stylekit-info-contrast-color: white;
--sn-stylekit-success-color: #2b9612;
--sn-stylekit-success-contrast-color: white;
--sn-stylekit-warning-color: #f6a200;
--sn-stylekit-warning-contrast-color: white;
--sn-stylekit-danger-color: #f80324;
--sn-stylekit-danger-contrast-color: white;
--sn-stylekit-shadow-color: #c8c8c8;
--sn-stylekit-background-color: white;
--sn-stylekit-border-color: #e3e3e3;
--sn-stylekit-foreground-color: black;
--sn-stylekit-contrast-background-color: #f6f6f6;
--sn-stylekit-contrast-foreground-color: #2e2e2e;
--sn-stylekit-contrast-border-color: #e3e3e3;
--sn-stylekit-secondary-background-color: #f6f6f6;
--sn-stylekit-secondary-foreground-color: #2e2e2e;
--sn-stylekit-secondary-border-color: #e3e3e3;
--sn-stylekit-secondary-contrast-background-color: #e3e3e3;
--sn-stylekit-secondary-contrast-foreground-color: #2e2e2e;
--sn-styleki--secondary-contrast-border-color: #a2a2a2;
--sn-stylekit-editor-background-color: var(--sn-stylekit-background-color);
--sn-stylekit-editor-foreground-color: var(--sn-stylekit-foreground-color);
--sn-stylekit-paragraph-text-color: #454545;
--sn-stylekit-input-placeholder-color: rgb(168, 168, 168);
--sn-stylekit-input-border-color: #e3e3e3;
--sn-stylekit-scrollbar-thumb-color: #dfdfdf;
--sn-stylekit-scrollbar-track-border-color: #e7e7e7;
--sn-stylekit-general-border-radius: 2px;
--sn-stylekit-monospace-font: 'Ubuntu Mono', courier, monospace;
--sn-stylekit-sans-serif-font: -apple-system, BlinkMacSystemFont, 'Segoe UI',
'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans',
'Helvetica Neue', sans-serif;
--sn-stylekit-grey-1: #72767e;
--sn-stylekit-grey-2: #bbbec4;
--sn-stylekit-grey-3: #dfe1e4;
--sn-stylekit-grey-4: #eeeff1;
--sn-stylekit-grey-4-opacity-variant: #bbbec43d;
--sn-stylekit-grey-5: #f4f5f7;
--sn-stylekit-grey-6: #e5e5e5;
--sn-stylekit-accessory-tint-color-1: #086dd6;
--sn-stylekit-accessory-tint-color-2: #ea6595;
--sn-stylekit-accessory-tint-color-3: #ebad00;
--sn-stylekit-accessory-tint-color-4: #7049cf;
--sn-stylekit-accessory-tint-color-5: #1aa772;
--sn-stylekit-accessory-tint-color-6: #f28c52;
}
```
In order to get SN to display a dock icon for your theme (a circle in the lower right corner of the app that allows you to quickly toggle themes), add the following payload into the your ext.json file when [publishing your theme](/extensions/publishing):
```json
"dock_icon": {
"type": "circle",
"background_color": "#086DD6",
"foreground_color": "#ffffff",
"border_color": "#086DD6"
}
```
### Reloading Mobile Themes
The mobile app will download a theme once and cache it indefinitely. If you're installing your own mobile theme and make changes, you can press and hold on the theme name in the list to bring up the option to re-download the theme from the server.
### 3.9.15 Changes
Since v3.9.15, the items in the notes list use a new variable for the background color, which will partially break the look of your theme when a note is selected or is hovered upon. In order to fix this, override the `--sn-stylekit-grey-5` color to one which fits your theme. You might also need to override the `--sn-stylekit-grey-4-opacity-variant` variable if the tags inside the note item don't look correct.
## Licensing
Our themes are provided open-source mainly for educational and quality purposes. You're free to install them on your own servers, but please consider subscribing to [Standard Notes Extended](https://standardnotes.com/extensions) to help sustain future development of the Standard Notes ecosystem.

View File

@@ -0,0 +1,175 @@
---
slug: configuration-options
id: configuration-options
title: Configuration options
sidebar_label: Configuration options
description: Manage options for your Standard Notes Standalone Infrastructure.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- self-hosting
- sync server
- configuration options
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Note
After changing any of the environment variables mentioned below you will have to restart the infrastructure in order for the changes to take effect. You can do so with the following command:
```bash
$ ./server.sh stop && ./server.sh start
```
## Syncing Server JS & Syncing Server Worker
All configuration options mentioned below are controlled by environment variables located in the `.env` file.
### Basics
- `LOG_LEVEL`: the level of logs outputted by the Syncing Server JS and Syncing Server JS Worker services.
- `NODE_ENV`: Node environment in which the service is running.
### Secrets
- `AUTH_JWT_SECRET`: secret used to sign the JWT tokens that are used for authorization & authentication purposes between services.
### Ports
- `EXPOSED_PORT`: the port on which the API Gateway will run. It is your main entry point for the entire infrastructure.
### Database
- `DB_HOST`: database host.
- `DB_REPLICA_HOST`: database replica host. If no replica is supported it should point to the same host as the primary DB.
- `DB_PORT`: database port.
- `DB_USERNAME`: database username.
- `DB_PASSWORD`: database password.
- `DB_DATABASE`: database name.
- `DB_DEBUG_LEVEL`: the level of logs which are outputted in the database context. Related to TypeORM.
- `DB_MIGRATIONS_PATH`: path to migrations folder that should be run against the database. Related to TypeORM.
### Cache
- `REDIS_URL`: url to Redis node.
### Redis Async Communication (Default)
- `REDIS_EVENTS_CHANNEL`: name of the Redis Pub/Sub channel used for communication between the service and its worker.
### AWS Async Communication (Optional)
If you do not want to use Redis as the communication channel between services, you can configure your async communication to be done via Amazon Web Services.
> **Note** We do not support configuring AWS secret and access keys in the environment variables as this is generally bad practice. If you would like to utilize SNS, SQS and S3, please configure an appropriate IAM user and role for the server on which you self-host your application as [best practice](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#sharing-credentials).
- `SNS_TOPIC_ARN`: ARN of the SNS topic that the service will publish events to.
- `SNS_AWS_REGION`: AWS region of the SNS topic.
- `SQS_QUEUE_URL`: URL of the SQS queue from which a worker will consume events.
- `SQS_AWS_REGION`: AWS region of the SQS queue.
- `S3_BACKUP_BUCKET_NAME`: name of the S3 bucket on which a file backup will be performed to transfer large data between services.
- `S3_AWS_REGION`: AWS region of the S3 bucket.
### Auth Service
-`AUTH_SERVER_URL`: url to the Auth service. Default value should be kept.
### Emails
- `EMAIL_ATTACHMENT_MAX_BYTE_SIZE`: Amount of bytes allowed for daily email backup attachments.
### Revisions
- `REVISIONS_FREQUENCY`: Amount of seconds that should pass between each save of a note for a new history revision to be created.
### New Relic (Optional)
We are utilizing New Relic to monitor our infrastructure. If you wish to set up your own monitoring in New Relic you can utilize the following environment variables:
- `NEW_RELIC_ENABLED`: enable or disable New Relic agent.
- `NEW_RELIC_APP_NAME`: name of the application to show in New Relic.
- `NEW_RELIC_LICENSE_KEY`: New Relic license key.
- `NEW_RELIC_NO_CONFIG_FILE`: should be true as we do not use configuration files for New Relic and fallback to environment variables.
- `NEW_RELIC_DISTRIBUTED_TRACING_ENABLED`: enable or disable distrubuted tracing.
- `NEW_RELIC_LOG_ENABLED`: enable or disable logs in New Relic.
- `NEW_RELIC_LOG_LEVEL`: level of logs in New Relic.
## Auth & Auth Worker
All configuration options mentioned below are controlled by environment variables located in the `docker/auth.env` file.
### Basics
- `LOG_LEVEL`: the level of logs outputted by the Auth and Auth Worker services.
- `NODE_ENV`: Node environment in which the service is running.
### Secrets
- `JWT_SECRET`: secret used to sign the JWT tokens that are used for authorization & authentication purposes between client and server.
- `LEGACY_JWT_SECRET`: This parameter is a fallback for supporting old client applications that had a different authorization mechanism. You don't need to change this if you are just starting to self-host your setup and do not own a legacy client application.
- `AUTH_JWT_TTL`: Time to live in seconds for the JWT token used for communication between services.
- `PSEUDO_KEY_PARAMS_KEY`: key used to generate password nonce in the process of creating user authentication parameters.
- `ENCRYPTION_SERVER_KEY`: key used for ecrypting user server key. Must be a hex string exactly 32 bytes long e.g. `feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308`.
### Authentication and Authorization
- `ACCESS_TOKEN_AGE`: time to live (in seconds) of the access token used to communicate with the server.
- `REFRESH_TOKEN_AGE`: time to live (in seconds) of the refresh token used to obtain a new access token.
- `EPHEMERAL_SESSION_AGE`: time to live (in seconds) of an ephemeral session. Used when you sign in without the "Stay signed in" option checked.
- `MAX_LOGIN_ATTEMPTS`: number of login attempts before locking the account.
- `FAILED_LOGIN_LOCKOUT`: lockout period in seconds after maximum failed login attempts.
### Redis Async Communication (Default)
- `REDIS_EVENTS_CHANNEL`: name of the Redis Pub/Sub channel used for communication between the service and its worker.
### Syncing Server Service
-`SYNCING_SERVER_URL`: url to the Syncing Server service. Default value should be kept.
### Disabling new user registrations
- `DISABLE_USER_REGISTRATION`: disable the option to register new users on the server.
### New Relic (Optional)
We are utilizing New Relic to monitor our infrastructure. If you wish to set up your own monitoring in New Relic you can utilize the following environment variables:
- `NEW_RELIC_ENABLED`: enable or disable New Relic agent.
- `NEW_RELIC_APP_NAME`: name of the application to show in New Relic.
- `NEW_RELIC_LICENSE_KEY`: New Relic license key.
- `NEW_RELIC_NO_CONFIG_FILE`: should be true as we do not use configuration files for New Relic and fallback to environment variables.
- `NEW_RELIC_DISTRIBUTED_TRACING_ENABLED`: enable or disable distrubuted tracing.
- `NEW_RELIC_LOG_ENABLED`: enable or disable logs in New Relic.
- `NEW_RELIC_LOG_LEVEL`: level of logs in New Relic.
## API Gateway
All configuration options mentioned below are controlled by environment variables located in the `docker/api-gateway.env` file.
### Basics
- `LOG_LEVEL`: the level of logs outputted by the API Gateway service.
- `NODE_ENV`: Node environment in which the service is running.
### Routing
- `SYNCING_SERVER_JS_URL`: url to the Syncing Server JS service.
- `AUTH_SERVER_URL`: url to the Auth service.
### New Relic (Optional)
We are utilizing New Relic to monitor our infrastructure. If you wish to set up your own monitoring in New Relic you can utilize the following environment variables:
- `NEW_RELIC_ENABLED`: enable or disable New Relic agent.
- `NEW_RELIC_APP_NAME`: name of the application to show in New Relic.
- `NEW_RELIC_LICENSE_KEY`: New Relic license key.
- `NEW_RELIC_NO_CONFIG_FILE`: should be true as we do not use configuration files for New Relic and fallback to environment variables.
- `NEW_RELIC_DISTRIBUTED_TRACING_ENABLED`: enable or disable distrubuted tracing.
- `NEW_RELIC_LOG_ENABLED`: enable or disable logs in New Relic.
- `NEW_RELIC_LOG_LEVEL`: level of logs in New Relic.

View File

@@ -0,0 +1,129 @@
---
slug: docker
id: docker
title: Self-hosting with Docker
sidebar_label: Docker
description: How to self-host the Standard Notes infrastructure with Docker.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- self-hosting
- sync server
- docker
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Requirements
These instructions make the following assumptions:
- The machine you will be running the infrastructure on has at least 2GB of memory.
- You've just finished setting up a Linux server (say, Ubuntu 20.04 64-bit) and have installed [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) on it.
- Due to mounted volumes we recommend running the setup as a root user. If you wish to run it as non-root user please remember about the [post-installation steps for Linux](https://docs.docker.com/engine/install/linux-postinstall#manage-docker-as-a-non-root-user).
- You've configured your security groups to allow for incoming SSH connections from your local IP.
- You've configured your security groups to allow for incoming TCP connections on port 80 and 443 from at least your local IP.
- You've configured a domain name (or subdomain) to point to your server's IP address.
## Getting started
> **Note** If you are a user with an already existing legacy database of the Syncing Server, we've prepared a [Migrating from Legacy guide](./legacy-migration.md).
SSH into your server and follow the steps below:
1. Make sure you are in your home directory and clone the [Standard Notes Standalone Infrastructure](https://github.com/standardnotes/standalone) project:
```bash
$ cd ~
$ git clone --single-branch --branch main https://github.com/standardnotes/standalone.git
$ cd standalone
```
1. Initialize default configuration files by typing:
```bash
$ ./server.sh setup
```
1. Customize your configuration
There are 5 environment variables that need to be filled in with generated secret keys:
- `AUTH_JWT_SECRET` in the `.env` file
- `JWT_SECRET`, `LEGACY_JWT_SECRET`, `PSEUDO_KEY_PARAMS_KEY`, and `ENCRYPTION_SERVER_KEY` in the `docker/auth.env` file
You can generate values for them by using:
```bash
$ openssl rand -hex 32
```
> **Note** The server must be restarted any time environment variables are changed.
1. (Optional) Customize the port
By default the syncing server will run on port 3000. If you have a different service running on that port, you can customize the port on which you want to run the infrastructure on. To do so, edit the `EXPOSED_PORT` variable in the `.env` file.
2. Simply run:
```bash
$ ./server.sh start
```
This should load all the microservices that the infrastructure consists of.
> **Note** The first run might take a few minutes as there are Docker images that need be pulled and built as well as migrations to be run for initializing the database.
3. Wait for the infrastructure to bootstrap
It takes a moment for the infrastructure to bootstrap and all the microservices to start. You can observe the process by typing:
```bash
$ ./server.sh logs
```
> **Note** You can safely escape from logs with CTRL+C
> **Note** Microservices depend on each other and start sequentially in our setup. In the logs you will likely observe that one service is waiting for another to start with lines like: "XYZ is unavailable yet - waiting for it to start" where XYZ is the dependent service name. This is expected.
Everything should be up and running once you observe that the `API Gateway` service has started by seeing the following line as one of the last ones in logs:
```
api-gateway_1 | {"message":"Server started on port 3000","level":"info"}
```
You can also check the state of all services via:
```bash
$ ./server.sh status
```
All services should be in `Up` state at this stage.
4. Test your access to the server locally:
You should be able now to check that the syncing server is running by checking `http://localhost:3000/healthcheck`:
```bash
$ curl http://localhost:3000/healthcheck
OK
```
> **Note** If you changed the `EXPOSED_PORT` variable you will have to check `http://localhost:{EXPOSED_PORT}/healthcheck`.
5. You're done!
## Securing Your Server
In order to start using your new server with the Standard Notes app at https://app.standardnotes.com you will have to configure an HTTPS reverse proxy.
Unless you already have an HTTP/HTTPS server running that will serve as a reverse proxy to the standalone infrastructure, head over to [Securing HTTP traffic of your Sync server](./https-support.md).
## Using your new server
In the account menu, choose `Advanced options` and enter the address of your new server in `Custom sync server`.
Then, register for a new account or log into an existing account and begin using your private new secure Standard Notes server!

View File

@@ -0,0 +1,58 @@
---
slug: file-uploads
id: file-uploads
title: File Uploads
sidebar_label: File Uploads
description: How to setup file uploading on your Standard Notes Standalone Server.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- self-hosting
- files
- uploads
- docker
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Requirements
In order to upload files you have to have an active subscription for your user. Read the [subscriptions](./subscriptions.md) page on instructions how to setup a subscription for yourself.
## Troubleshooting
### Files Server URL
In your `.env` file the environment variable `FILES_SERVER_URL` has to be set to a publicly accessible url. The reason for that is that the clients are accessing the Files Server directly instead of via Api Gateway. Remember that if you are hosting your standalone instance on an external server then `localhost` is not the host that properly describes where the files server resides.
### Upload directory write permissions
The default upload directory is located inside the standalone folder under `data/uploads`. Depending on the running OS, you might encounter write permissions to that folder by the application. In that case the following commands might help:
```bash
chmod -R 775 data
mkdir -p data/uploads
sudo chmod -R 755 data/uploads
sudo chown -R 1001.1001 data/uploads
```
### Limiting Storage Quota
If you would like to limit the file upload quota for your user then make sure to run the following query on your database:
```sql
INSERT INTO subscription_settings(uuid, name, value, created_at, updated_at, user_subscription_uuid) VALUES (UUID(), "FILE_UPLOAD_BYTES_LIMIT", 10737418240, FLOOR(UNIX_TIMESTAMP(NOW(6))*1000000), FLOOR(UNIX_TIMESTAMP(NOW(6))*1000000), (SELECT us.uuid FROM user_subscriptions us INNER JOIN users u ON us.user_uuid=u.uuid WHERE u.email="EMAIL@ADDR"));
```
Note that this is setting the limit to 10GB (10737418240 bytes) for user with email `EMAIL@ADDR`
### CloudFlare Missing Headers
When using CloudFlare in conjuction with Nginx you might encounter an issue about missing `Accept-Ranges` header which is required for file downloading. As a fix please add this to your Nginx configuration:
```
proxy_cache off;
```

View File

@@ -0,0 +1,43 @@
---
slug: getting-started
id: getting-started
title: Getting Started with Self-hosting
sidebar_label: Getting Started
description: How to get started with self-hosting Standard Notes Infrastructure.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- self-hosting
- sync server
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
Our self-hosted server infrastructure consists of several different microservices that are responsible for different sets of functionality. Our self-hosted server is only intended as the backend that processes and stores your data; it does not include self-hosting the web application, which is an optional process that must be done separately. You will be able to use our existing [web](https://app.standardnotes.com) and desktop app with your self-hosted server.
## Get Started
👉 **[Using our automated docker-compose setup with accompanying scripts](./docker.md)** 👈
If you'd like to learn more about each of the particular services, head over to [Infrastructure Overview](./infrastructure-overview.md).
> **Note** Our setup also provides a running MySQL database and a Redis cache node. You do not have to provision these services on your own. For users that have been self-hosting a legacy version of our server, we've prepared a [Migrating from Legacy guide](./legacy-migration.md).
### Recommendations
We highly recommend you use our Docker setup to host your syncing server. Docker containers are isolated software environments that you can control and manage.
If you are new to Docker, please see the [official Docker documentation](https://docs.docker.com/get-started) on how to get started. Ensure you [install Docker-Compose](https://docs.docker.com/compose/install/) following the documentation. Your Linux distribution may not have the most up to date docker-compose and will fail to load.
We recommend avoiding setting up your syncing server from scratch with Nginx unless you are proficient with Nginx. Setting up the full architecture can be challenging without full knowledge of how the syncing server and its microservices function.
### Issues
If you have any issues with setting up your syncing server, please [open an issue on GitHub](https://github.com/standardnotes/standalone/issues).
## Web application
If you would like to self-host the actual Standard Notes web application, visit the [repository for the web app on GitHub](https://github.com/standardnotes/web).

View File

@@ -0,0 +1,93 @@
---
slug: https-support
id: https-support
title: Securing HTTP traffic of your Sync server
sidebar_label: Securing HTTP traffic of your Sync server
description: How to secure HTTP traffic of your Standard Notes Sync server.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- self-hosting
- sync server
- secure http traffic
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
### Introduction
These instructions will enable you to secure HTTP traffic of your standalone infrastructure, using a reverse proxy with `Nginx`.
#### Pre-requisites
- Your standalone infrastructure is running on our [docker](./docker.md) setup
- You've installed `nginx` in your server.
- You've configured a domain name (or subdomain) to point to your server's IP address.
### Getting started
#### Setting up Nginx
1. Disable the default virtual host:
```bash
unlink /etc/nginx/sites-enabled/default
```
1. Create a new file named `standardnotes.conf` within `/etc/nginx/sites-available`:
```
server {
listen 80;
listen [::]:80;
server_name yourdomain.com;
access_log /var/log/nginx/standardnotes-access.log;
error_log /var/log/nginx/standardnotes-error.log;
client_max_body_size 50M;
location / {
proxy_pass http://127.0.0.1:3000;
}
}
```
> **Note** Replace `yourdomain.com` with your actual domain and `3000` with the port you have specified as `{EXPOSED_PORT}` if you have changed it.
1. Enable your new site:
```bash
ln -s /etc/nginx/sites-available/standardnotes.conf /etc/nginx/sites-enabled/standardnotes.conf
```
1. Restart Nginx to apply changes
There may be different ways to restart Nginx. If you installed Nginx from Ubuntu's default repository just type:
```bash
$ sudo service nginx restart
```
1. Test your `Nginx` configuration with:
```bash
$ nginx -t
```
1. Setting up Certbot for HTTPS configuration
Go to [certbot](https://certbot.eff.org/instructions) to get and install your HTTPS certificate.
Certbot should automatically update your Nginx configuration and create SSL certificates for you.
After completing the above instructions, your Sync server should be HTTPS enabled!
## Using your secured server
In the account menu, choose `Advanced Options` and enter the address of your new server in `Sync Server Domain`.
Then, register for a new account or log into an existing account and begin using your private new secure Standard Notes server!

View File

@@ -0,0 +1,53 @@
---
slug: infrastructure-overview
id: infrastructure-overview
title: Infrastructure Overview
sidebar_label: Infrastructure Overview
description: Standard Notes Infrastructure Overview.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- self-hosting
- sync server
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Services
The Syncing Server infrastructure consists of a few different microservices that are responsible for different sets of functionality.
### Syncing Server JS
Syncing Server JS is a TypeScript implementation of our Syncing Server. This is the core of our business logic that is responsible for all operations on user data.
### Syncing Server JS Worker
Syncing Server JS Worker is responsible for all asynchronous tasks that the Syncing Server JS may offload for background processing. This includes for example processing of email backups, resolving issues with note duplicates, sending notes to extensions server, and more.
### Auth
This server is responsible for all authorization and authentication mechanisms. Auth is where all account-related metadata is handled and processed.
### Auth Worker
Similar to Syncing Server JS Worker, Auth Worker is responsible for all asynchronous tasks related to the domain of authentication and authorization. For example, processing account deletion requests and users' post-registration tasks.
### API Gateway
This is the main "entry point" of the entire architecture. API Gateway serves as a router and proxy to all services which are inaccessible directly. All requests from client applications will have to go through API Gateway in order to reach a certain underlying service.
This service will be paired with your reverse proxy for [HTTPS support](./https-support.md)
### DB
MySQL database server. This is where all data is stored.
### Cache
Redis cache node where all temporary data is persisted for performance optimization and auto-expiring features.
In self-host mode, Redis is used by default as a communication queue between services and their workers.

View File

@@ -0,0 +1,47 @@
---
slug: legacy-migration
id: legacy-migration
title: Migrating From Legacy
sidebar_label: Migrating From Legacy
description: Migrating From Legacy Guide
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- self-hosting
- sync server
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Preparing a database dump
If you have previously self-hosted your setup with our legacy Syncing Server, you will need to first dump the data from your existing database. There are two ways to do this depending on whether you had a separate database or the one we provided with our Docker setup.
### Database from Docker setup
In order to create a database dump, use the following command:
```bash
$ docker exec your_db_container_id sh -c 'exec mysqldump your_database_name -uroot -p "your_password"' > ./dbdump.sql
```
> **Note:** In order to get the value of `your_db_container_id`, run `docker ps` to inspect your running containers. `your_database_name` and `your_password` have to be changed to whatever you had set up in your .env file.
### External database
Run:
```bash
mysqldump your_database_name -h your_host -u your_user -p "your_password" > ./dbdump.sql
```
> **Note** Change `your_host`, `your_database_name`, `your_user` and `your_password` to values that you use to connect to your database.
## Importing your database dump
In order to import your data to the database that our Standalone setup will create, just place your `dbdump.sql` file inside the `path-to-your-standalone/data/import` folder. The data will be imported once the setups starts.
To proceed, head over to our [Self-hosting with Docker](./docker.md) page.

View File

@@ -0,0 +1,30 @@
---
slug: subscriptions
id: subscriptions
title: Subscriptions on your Standard Notes Standalone Server
sidebar_label: Subscriptions
description: How to add subscriptions on your Standard Notes Standalone Server.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- self-hosting
- updating
- sync server
- docker
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Requirements
To add a subscription to your self-hosted user account, run the following commands (Replace EMAIL@ADDR with your user email):
```bash
./server.sh create-subscription EMAIL@ADDR
```
## ✨ You Should Know
Building Standard Notes has high costs. If everyone evaded contributing financially, we would no longer be here to continue to build upon and improve these services for you. Please consider [donating](https://standardnotes.com/donate) if you do not plan on purchasing a subscription.

View File

@@ -0,0 +1,51 @@
---
slug: updating
id: updating
title: Updating Standard Notes Standalone Infrastructure
sidebar_label: Updating
description: How to update Standard Notes Standalone Infrastructure.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- self-hosting
- updating
- sync server
- docker
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Requirements
These instructions make the following assumptions:
- You have an existing standalone infrastructure running with our [docker setup](./docker.md)
## Updating
Updating of the infrastructure essentially consists of:
- Stopping all services
- Pulling changes from Git
- Checking for env file changes: new environment variables might have been added that you will need to configure
- Downloading latest Docker image versions of Standard Notes services
- Starting the services up again
To save you all the trouble we've packed it all nicely in one command that you run by typing:
```bash
$ ./server.sh update
```
## Troubleshooting
If you encounter any problems while updating, you can nuke your setup and start over. But **you must backup your data** inside the `data/*` folder. Then, to wipe your existing setup, run:
```bash
$ ./server.sh cleanup
```
> ***WARNING*** this will permanently delete all your data, so be sure to back up your database before running this command.

View File

@@ -0,0 +1,303 @@
---
id: auth
title: Auth API
sidebar_label: Auth
description: Authentication portion of the Standard Notes API.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- sync
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
### POST /auth
**Registers a new user**
| | | |
|-------------|---------|---------------------------------------------------------------------------------------------------------------------|
| api | `string` | The API version to use. Must not be null, otherwise oldest API version used. Latest value is "20200115" |
| created | `string` | Integer timestamp representing the date the client generated the account's encryption keys. Example "1622494310383" |
| email | `string` | The account email. |
| ephemeral | `boolean` | Whether the initial session created for this account is ephemeral ("Stay signed in" unchecked). |
| identifier | `string` | Should equal the account email. |
| origination | `string` | Should equal "registration" |
| password | `string` | The server password generated by the client from the user's master password. |
| pw_nonce | `string` | The nonce generated by the client for the user's encryption key. |
| version | `string` | The protocol version the client used to generate the user's encryption key. Latest is "004". |
#### Response
```JSON
{
"session": {
"access_token": "1:457b1f4b-88c2-4328-bf32-fe7dd9431d62:WwKgTEDUoEhxECu6",
"refresh_token": "1:457b1f4b-88c2-4328-bf32-fe7dd9431d62:G-qEoZsFtoj~RLL-",
"access_expiration": 1627678312000,
"refresh_expiration": 1654051238000
},
"key_params": {
"created": "1622494310383",
"identifier": "foo@example.com",
"origination": "registration",
"pw_nonce": "d97ed41c581fe8c3e0dce7d2ee72afcb63f9f461ae875bae66e30ecf3d952900",
"version": "004"
},
"user": {
"uuid": "682f9deb-b75f-4d97-91fa-6fd82a482db1",
"email": "foo@example.com"
}
}
```
### POST /auth/sign_in
**Authenticates a user and returns a session.**
| | | |
|-------------|---------|---------------------------------------------------------------------------------------------------------------------|
| api | `string` | The API version to use. Must not be null, otherwise oldest API version used. Latest value is "20200115" |
| email | `string` | The account email. |
| ephemeral | `boolean` | Whether the session created for this account is ephemeral ("Stay signed in" unchecked). |
| password | `string` | The server password generated by the client from the user's master password. |
#### Response
```JSON
{
"session": {
"access_token": "1:ee03808d-dd05-417c-9c87-d471e49bcc49:Q~-UoIpDhdtkii_t",
"refresh_token": "1:ee03808d-dd05-417c-9c87-d471e49bcc49:Jn8UWzeHx2H5nZI5",
"access_expiration": 1627678779000,
"refresh_expiration": 1654051705000
},
"key_params": {
"created": "1622494310383",
"identifier": "foo@example.com",
"origination": "registration",
"pw_nonce": "d97ed41c581fe8c3e0dce7d2ee72afcb63f9f461ae875bae66e30ecf3d952900",
"version": "004"
},
"user": {
"uuid": "682f9deb-b75f-4d97-91fa-6fd82a482db1",
"email": "foo@example.com"
}
}
```
### GET /auth/params
**Queries the parameters used for key generation for an email address. Queried before signing into an account.**
| | | |
|-------------|---------|---------------------------------------------------------------------------------------------------------------------|
| api | `string` | The API version to use. Must not be null, otherwise oldest API version used. Latest value is "20200115" |
| email | `string` | The account email. |
#### Response
```JSON
{
"identifier": "foo@example.com",
"pw_nonce": "d97ed41c581fe8c3e0dce7d2ee72afcb63f9f461ae875bae66e30ecf3d952900",
"version": "004"
}
```
### POST /auth/change_pw
**Changes a user's password.**
| | | |
|-------------|---------|---------------------------------------------------------------------------------------------------------------------|
| api | `string` | The API version to use. Must not be null, otherwise oldest API version used. Latest value is "20200115" |
| created | `string` | Integer timestamp representing the date the client generated the account's new encryption keys. Example "1622494310383" |
| identifier | `string` | The account email. |
| origination | `string` | Should equal "password-change" |
| current_password | `string` | The old server password generated by the client from the user's master password. |
| new_password | `string` | The new server password generated by the client from the user's master password. |
| pw_nonce | `string` | The nonce generated by the client for the user's encryption key. |
| version | `string` | The protocol version the client used to generate the user's encryption key. Latest is "004". |
#### Response
```JSON
{
"session": {
"access_token": "1:27d4fd8f-b730-4e0a-afd3-1600fb466aaa:HMgmZwV5k5ePt0vj",
"refresh_token": "1:27d4fd8f-b730-4e0a-afd3-1600fb466aaa:wHuTeOB-qWGP3AE3",
"access_expiration": 1627679129000,
"refresh_expiration": 1654052055000
},
"key_params": {
"created": "1622494310383",
"identifier": "foo@example.com",
"origination": "password-change",
"pw_nonce": "be1974ff6fb1c541aa8c71fd3c66851b6492cf224b661c72daf44e0bef3096bb",
"version": "004"
},
"user": {
"uuid": "682f9deb-b75f-4d97-91fa-6fd82a482db1",
"email": "foo@example.com"
}
}
```
## Sessions
### Session Model
| Field | Type | Description |
|----------------|-----------|---------------------------------------------------|
| uuid | `string` | Unique identifier of the session |
| user_uuid | `string` | Unique identifier of the user |
| user_agent | `string` | The user agent used to create the session |
| api_version | `string` | The server API version used to create the session |
| access_token | `string` | The access token used to authenticate requests |
| refresh_token | `string` | The refresh token used to extend tokens |
| access_expiration | `datetime` | The expiration time of the access token |
| refresh_expiration | `datetime` | The expiration time of the refresh token |
| created_at | `datetime` | Date and time of creation of the session |
| updated_at | `datetime` | Last updated date and time of the session |
- Each `session` includes the API version they were created with. This way we can deny sessions for a given API version if we detect a vulnerability with that version in the future
- Sessions are created in the following cases:
- When a user signs in
- When a user registers a new account
---
### Authenticated requests
The `Authorization` request header field is used by clients to make authenticated request. `Bearer` is the only authentication scheme allowed.
The client must send the `access` token generated by the session, in the `Authorization` header. For example:
```
GET /sessions HTTP/1.1
Host: sync.standardnotes.org
Authorization: Bearer <access token>
```
---
Below is a list of endpoints related to session management:
| Method | URL | Params | Description | Successful response code |
|-----------|--------------------------|---------------------|--------------------------------------------------------|--------------------------|
|`POST` | /auth/sign_out | *None* | Terminates the current session | `204` |
|`DELETE` | /session | **uuid** | Terminates the specified session by UUID | `204` |
|`DELETE` | /sessions | *None* | Terminates all sessions, except the current one | `204` |
|`GET` | /sessions | *None* | Lists all sessions active sessions | `200` |
|`POST` | /session/token/refresh | **refresh_token** | Obtains new pair of `access_token` and `refresh_token` | `200` |
A successful request to `GET /sessions` returns the following JSON response:
```json
{
"sessions": [
{
"uuid": "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx",
"user_agent": "<product> / <product-version> <comment>",
"api_version": "xxxxyyzz",
"current": "<boolean>",
"created_at": "2020-01-01T00:00:00.000Z"
}
...
]
}
```
#### Obtaining tokens
Tokens can be obtained every time the user performs any of the following actions:
1. When a user signs in
1. When a user registers an account
1. When the tokens are refreshed
#### Refreshing tokens
When an expired `access_token` is provided in the `Authorization` HTTP header, the following JSON response is returned:
HTTP Status Code: `498 Expired Access Token`
```json
{
"error": {
"tag": "expired-access-token",
"message": "The provided access token has expired."
}
}
```
To continue accessing resources, the `access_token` must be refreshed. That is, the current `access_token` is replaced with a new one with an extended expiration date.
To refresh an `access_token`, a valid `refresh_token` is needed. This `refresh_token` must meet the following requirements:
- It should belong to the session of the `access_token`
- It should not be expired
Since the `refresh_token` is of single-use, a new `refresh_token` is obtained when the `access_token` is refreshed.
Refreshing tokens is a process that is transparent to the user, meaning that the client will perform the requests to keep valid tokens without user intervention.
Here's how to refresh tokens:
1. Send a `POST` request to `/session/token/refresh`. The body should contain a JSON paylaod with the `refresh_token`:
```
POST /session/token/refresh HTTP/1.1
Host: sync.standardnotes.org
Authorization: Bearer <access token>
{
"refresh_token": "R_xxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx"
}
```
1. The `refresh_token` is validated on the server. Depending of the circumstances, there should be two outcomes:
1. The provided `refresh_token` is valid. If so, a new pair of tokens is generated and the following JSON response is returned:
```json
{
"token": "xxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx",
"session": {
"refresh_expiration": 1583020800,
"refresh_token": "xxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx",
}
}
```
1. The provided `refresh_token` is expired. If so, the following JSON response is returned:
HTTP Status Code: `400 Bad Request`
```json
{
"error": {
"tag": "expired-refresh-token",
"message": "The refresh token has expired."
}
}
```
User must start a new session by re-entering their credentials.
### Expiration
#### Sessions
Sessions should be terminated after a period of inactivity. This is for best security practices.
`Long-lived sessions` are a good choice for our use case, because it can build a better user experience than expiring sessions for a short idle-timeout.
- A `session` that remains inactive for `1 year` will be terminated, along with all `tokens`
#### Tokens
| Name | Type | Expiration |
|-----------|-------------|---------------|
| `access` | Short-lived | `60 days` |
| `refresh` | Long-lived | `1 year` |
- A `refresh` token is of single-use, while an `access` token can be used as long as it is not expired

View File

@@ -0,0 +1,162 @@
---
slug: encryption/003
id: encryption-003
title: Encryption Protocol Specification v003
sidebar_label: Encryption v003
description: Specification for the Standard Notes end-to-end encryption.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- encryption specification
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Version 0.0.3
It is important that there exist a separation of concerns between the server and the client. That is, the client should not trust the server, and vice versa.
Encryption keys are generated by stretching the user's input password using a [key derivation function.](https://en.wikipedia.org/wiki/Key_derivation_function)
The resulting key is split in three — the first third is sent to the server as the user's password, the second third is saved locally as the user's master encryption key, and the last third is used as an authentication key. In this setup, the server is never able to compute the encryption key or the user's original password given just a fraction of the resulting key.
Note: client-server connections must be made securely through SSL/TLS.
#### Elaboration on User model encryption related fields
| name | details |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| pw_cost | The number of iterations to be used by the KDF. The minimum for version 003 is 100,000. However note that non-native clients (web clients not using WebCrypto) will not be able to handle any more than 3,000 iterations. |
| pw_nonce | A nonce for password derivation. This value is initially created by the client during registration. |
## Key Generation
### Client Instructions
Given a user inputted password `uip`, the client's job is to generate a password `pw` to send to the server, a master key `mk` that the user stores locally to encrypt/decrypt data, and an auth key `ak` for authenticating encrypted data.
#### Login Steps
1. Client makes GET request with user's email to `auth/params` to retrieve password nonce, cost, and version.
1. Client verifies cost >= minimum cost (100,000 for 003.)
1. Client computes `pw`, `mk`, and `ak` using PBKDF2 with SHA512 as the hashing function and output length of 768 bits:
```
salt = SHA256:Hexdigest([email, "SF", version, pw_cost, pw_nonce].join(":"))
key = pbkdf2(uip, salt, sha512, 768, pw_cost) // hex encoded
pw = key.substring(0, key.length/3)
mk = key.substring(key.length/3, key.length/3)
ak = key.substring(key.length/3 * 2, key.length/3)
```
1. Client sends `pw` to the server as the user's "regular" password and stores `mk` and `ak` locally. (`mk` and `ak` are never sent to the server).
#### Registration Steps
1. Client chooses default for `pw_cost` (minimum 100,000).
1. Client generates `pw_nonce`:
```
pw_nonce = random_string(256)
```
1. Client computes `pw`, `mk`, and `ak` using step (3) from Login Steps.
1. Client registers with `email`, `pw`, `pw_cost`, `pw_nonce`, and `version`.
## Item Encryption
In general, when encrypting a string, one should use an IV so that two subsequent encryptions of the same content yield different results, and one should authenticate the data as to ascertain its authenticity and lack of tampering.
In Standard Notes, two strings are encrypted for every item:
- The item's `content`.
- The item's `enc_item_key`.
## Client-side
An item is encrypted using a random key generated for each item.
### Encryption:
Note that when encrypting/decrypting data, keys should be converted to the proper format your platform function supports. It's best to convert keys to binary data before running through any encryption/hashing algorithm.
For every item:
1. Generate a random 512 bit key `item_key` (in hex format).
2. Split `item_key` in half; set item encryption key `item_ek = first_half` and item authentication key `item_ak = second_half`.
3. Encrypt `content` using `item_ek` and `item_ak` following the instructions "Encrypting a string using the 003 scheme" below and send to server as `content`.
4. Encrypt `item_key` using the global `mk` and global `ak` following the instructions "Encrypting a string using the 003 scheme" below and send to server as `enc_item_key`.
### Decryption:
Check the first 3 characters of the `content` string. This will be the encryption version.
- If it is equal to "001", which is a legacy scheme, decrypt according to the 001 instructions found [here](https://github.com/standardfile/standardfile.github.io/blob/master/doc/spec-001.md).
- If it is equal to "002" or "003", decrypt as follows:
1. Decrypt `enc_item_key` using the global `mk` and global `ak` according to the "Decrypting a string using the 003 scheme" instructions below to get `item_key`.
2. Split `item_key` in half; set encryption key `item_ek = first_half` and authentication key `item_ak = second_half`.
3. Decrypt `content` using `item_ek` and `item_ak` according to the "Decrypting a string using the 003 scheme" instructions below.
### Encrypting a string using the 003 scheme:
Given a `string_to_encrypt`, an `encryption_key`, and an `auth_key`:
1. Generate a random 128 bit string called IV.
1. Encrypt `string_to_encrypt` using `AES-CBC-256:Base64`, `encryption_key`, and `IV`:
```
ciphertext = AES-Encrypt(string_to_encrypt, encryption_key, IV)
```
1. Generate `string_to_auth` by combining the encryption version (003), the item's UUID, the IV, and the ciphertext using the colon ":" character:
```
string_to_auth = ["003", uuid, IV, ciphertext].join(":")
```
1. Compute `auth_hash = HMAC-SHA256:Hex(string_to_auth, auth_key)`.
1. Generate the final result by combining the five components into a `:` separated string:
```
result = ["003", auth_hash, uuid, IV, ciphertext].join(":")
```
### Decrypting a string using the 003 scheme:
Given a `string_to_decrypt`, an `encryption_key`, and an `auth_key`:
1. Split the string into its constituent parts: `components = string_to_decrypt.split(":")`.
1. Assign local variables:
```
version = components[0]
auth_hash = components[1]
uuid = components[2]
IV = components[3]
ciphertext = components[4]
```
1. Ensure that `uuid == item.uuid`. If not, abort decryption.
1. Generate `string_to_auth = [version, uuid, IV, ciphertext].join(":")`.
1. Compute `local_auth_hash = HMAC-SHA256(string_to_auth, auth_key)`. Compare `local_auth_hash` to `auth_hash`. If they are not the same, skip decrypting this item, as this indicates that the string has been tampered with.
1. Decrypt `ciphertext` to get final result: `result = AES-Decrypt(ciphertext, encryption_key, IV)`.
## Server-side
For every received item:
1. (Optional but recommended) Encrypt `content` using server known key and store. Decrypt before sending back to client.
## Next Steps
Join the [Slack group](https://standardnotes.com/slack) to discuss implementation details and ask any questions you may have.
You can also email [help@standardnotes.org](mailto:help@standardnotes.org).
Follow [@standardnotes on Twitter](https://twitter.com/standardnotes) for updates and announcements.

View File

@@ -0,0 +1,345 @@
---
slug: encryption
id: encryption
title: Client Encryption API
sidebar_label: Client Encryption
description: Specification for the Standard Notes end-to-end encryption.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- encryption specification
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
The 004 protocol upgrade centers around a system that makes it easy and painless to upgrade to a future protocol version, as well as more modern cryptographic primitives.
This page is a copy of the specification file located at [github.com/standardnotes/snjs](https://github.com/standardnotes/snjs/blob/master/packages/snjs/specification.md).
## Introduction
The Standard Notes Protocol describes a set of procedures that ensure client-side encryption of data in such a way that makes it impossible for the server, which houses the data, to read or decrypt the data. It treats the server as a dumb data-store that simply saves and returns values on demand.
Even in scenarios when the server is under active attack, clients should be fully protected, and cannot be tricked into revealing any sensitive information.
The client and server communicate under two common procedures: authentication, and syncing.
Authentication is a one-time transfer of information between client and server. In short, clients generate a long secret key by stretching a user-inputted password using a KDF. The first half of that key is kept locally as the "master key" and is never revealed to the server. The second half of that key is sent to the server as the "account server password".
The master key is then used to encrypt an arbitrary number of items keys. Items keys are generated randomly and not based on the account password. Items keys are used to encrypt syncable data, like notes, tags, and user preferences. Items keys themselves are also synced to user accounts, and are encrypted directly with the master key.
When a user's master key changes, all items keys must be re-encrypted with the new master key. Accounts should generally have one items key per protocol version, so even in the event where many protocol upgrades are created, only a few KB of data must be re-encrypted when a user's credentials change (as opposed to completely re-encrypting many megabytes or gigabytes of data).
Data is also encrypted client-side for on-device storage. When an account is present, all local data is encrypted by default, including simple key-value storage (similar to a localStorage-like store). Persistence stores are always encrypted with the account master key, and the master key is stored in the device's secure keychain (when available).
Clients also have the option of configuring an application passcode, which wraps the account master key with an additional layer of encryption. Having a passcode enabled is referred to as having a "root key wrapper" enabled. When a root key is wrapped, it is stored in local storage as an encrypted payload, and the keychain is bypassed. This allows for secure key storage even in environments that don't expose a keychain, such as web browsers.
This document delineates client-side procedures for key management and generation, data encryption, and storage encryption. Concepts related to server syncing and server session management are outside the scope of this document. This document however wholly covers any values that a server would receive, so even though syncing and server session management is out of scope, the procedures outlined in this document should guarantee that no secret value is ever revealed to the server.
## Key Management
**There are three main concepts as related to keys:**
1. **A root key**—based on an account's user-inputted password. There exists only one root key per account.
2. **A root key wrapper**—_wraps_ a root key (encrypts it) with an additional layer. This is a local-only construct, and translates directly as an "application passcode" feature.
3. **Items keys**—used to encrypt items. There can exist many items keys, and one items key can encrypt many items. Each items key is encrypted with the root key. When the root key changes, all items keys must be re-encrypted using the new root key.
### Key Generation Flow
1. User registers with an email (`identifier`) and a `password`.
2. `password` is run through a KDF to generate a key, which is then split in two, as part of a single `rootKey`.
1. The first half is the `masterKey`.
2. The second half is the `serverPassword`.
3. Client registers user account with server using `email` and `rootKey.serverPassword`.
4. Client creates new random key `itemsKey`. This key is encrypted directly with `rootKey.masterKey`, and the encrypted `itemsKey` is assigned a UUID and uploaded to the user's account. (Each `itemsKey` is a traditional item, just like a note or tag.)
### Password change or protocol upgrade flow
**When a user changes their password, or when a new protocol version is available:**
1. Client generates new `rootKey` using account identifier and password, and thus generates new `rootKey.masterKey`, `rootKey.serverPassword`, and `keyParams`, which include the protocol version and other public information used to guide clients on generating the `rootKey` given a user password.
2. Client submits new `rootKey.serverPassword` and `keyParams` to server. Note that the changing the `serverPassword` does not necessarily invalidate a user's session. Sessions management is outside of the scope of this document.
3. Client loops through all `itemsKeys` and re-encrypts them with new `rootKey.masterKey`. All `itemsKeys` are then re-uploaded to server. Note that `itemsKeys` are immutable and their inner key never changes. The key is only re-encrypted using the new `masterKey`.
This flow means that when a new protocol version is available or when a user changes their password, we do not need to re-encrypt all their data, but instead only a handful of keys.
### Key Rotation
By default, upgrading an account's protocol version will create a new `itemsKey` for that version, and that key will be used to encrypt all data going forward. To prevent large-scale data modification that may take hours to complete, any data encrypted with a previous `itemsKey` will be re-encrypted with the new `itemsKey` progressively, and not all at once. This progressive re-encryption occurs when an item is explicitly modified by the user. Applications can also be designed to bulk-modify items during idle-capacity, without user interaction.
**When changing the account password:**
- If a new protocol version is available, changing the account password will also upgrade to the latest protocol version and thus generates a new default `itemsKey`.
- If no new protocol version is available, or if the user is already using the latest version, changing the account password generates a new `rootKey`, as well as generates a new `itemsKey`. The new `itemsKey` will be used as the default items encryption key, and will also be used to progressively re-encrypt previous data. Generating a new `itemsKey` on password change ensures backward secrecy in the case the previous account password is compromised.
## Encryption Flow
_For each_ item (such as a note) the client wants to encrypt:
1. Client generates random `item_key` (note: singular. Not related to `itemsKey`).
2. Client encrypts note content with `item_key` to form `content`.
3. Client encrypts `item_key` with default `itemsKey` as `enc_item_key`.
4. Client notes `itemsKey` UUID and associates it with encrypted item payload as `items_key_id`, and uploads payload to server.
To decrypt an item payload:
1. Client retrieves `itemsKey` matching `items_key_id` of payload.
2. Client decrypts item's `enc_item_key` with `itemsKey` to form `item_key`.
3. Client decrypts item's `content` using `item_key`.
## Authentication
Registering for an account involves generating a `rootKey` and respective `keyParams`, according to the key generation flow above. The key parameters are uploaded to the server, and include:
- unique identifier (email)
- salt seed
- protocol version
To sign into an account, clients first make a request to the server to retrieve the key params for a given email. This endpoint is public and non-authenticated (unless the account has two-factor authentication enabled). The client then uses the retrieved key params to generate a `rootKey`, and uses the `rootKey.serverPassword` to authenticate the account.
Note that by default, the client trusts the protocol version the server reports. The client uses this protocol version to determine which cryptographic primitives (and their parameters) to use for key generation. This raises the question of, what happens if a malicious server underreports an account's version in order to weaken key generation parameters? For example, if a user's account is 004, but the server reports 002, the client will proceed to generate a `serverPassword` using outdated primitives.
There are two safeguards against this scenario:
1. Older protocol versions are expired and become no longer supported after a certain period.
2. Clients may sign in with a flag known as "strict sign in" (SSI). SSI ensures that the client _always_ signs in with the client-side _hardcoded latest version_ of the protocol. For example, if a client with SNJS 004 support attempts to sign in with SSI enabled, and the server reports a protocol version of 002 for a given account, the client will refuse this sign-in, and will not proceed with key generation. SSI is a user-controlled option. Clients cannot be programmed to default to SSI, as otherwise, users would be unable to sign in to their account whenever a new protocol version is available.
## Root Key Wrapping
Root key wrapping is a local-only construct that pertains to how the root key is stored locally. By default, and with no root key wrapping, the `rootKey` is stored in the secure device keychain. Only the `rootKey.masterKey` is stored locally; the `rootKey.serverPassword` is never stored locally, and is only used for initial account registration. If no keychain is available (web browsers), the `rootKey` is stored in storage in necessarily plain format.
Root key wrapping allows the client to encrypt the `rootKey` before storing it to disk. Wrapping a root key consists of:
1. Client asks user to choose a "local passcode".
2. The local passcode is run through the same key generation flow as account registration (using a random UUID as the account identifier, in place of an email) to generate a separate new root key known as the `rootKeyWrappingKey` (which likewise consists of a `masterKey` and an unused `serverPassword`).
3. The `rootKeyWrappingKey` is used to encrypt the `rootKey` as `wrappedRootKey`. The `wrappedRootKey` (along with `wrappingKeyKeyParams`) is stored directly in storage, and the keychain is cleared of previous unwrapped `rootKey`. (Some keychains have fixed payload size limit, so an encrypted payload may not always fit. For this reason `wrappedRootKey` is always stored directly in storage.)
**To unwrap a root key:**
1. Client displays an "Enter your local passcode" prompt to user.
2. Client runs user-inputted password through key generation scheme (using stored `wrappingKeyKeyParams`) to generate a temporary `rootKeyWrappingKey`.
3. Client attempts to decrypt `wrappedRootKey` using `rootKeyWrappingKey`. If the decryption process succeeds (no errors are thrown), the client successfully unlocks application, and keeps the unwrapped `rootKey` in application memory to aid in encryption and decryption of items (or rather `itemsKeys`, to be exact).
**The purpose of root key wrapping is many-fold:**
1. To allow for secure storage of root key when no secure keychain is available (i.e web browsers).
2. Even in cases when a keychain is available, root key wrapping allows users to choose an arbitrary password to protect their storage with.
3. To allow for encryption of local storage.
4. To allow applications to introduce cryptographically-backed UI-level app locking.
When a root key is wrapped, no information about the wrapper is persisted locally or in memory beyond the `keyParams` for the wrapper. This includes any sort of hash for verification of the correctness of the entered local passcode. That is, when a user enters a local passcode, we know it is correct not because we compare one hash to another, but by whether it succeeds in decrypting some encrypted payload.
## Multi-Client Root Key Changes
Because account password changes (or, in general, root key changes) require all existing items keys to be re-encrypted with the new root key, it is possible that items keys eventually fall into an inconsistent state, such that some are encrypted with a newer root key, while others are encrypted with the new root key. Clients encountering an items key they cannot encrypt with the current account root key parameters would then reach a dead end, and users would see undecryptable data.
To recover the ability to decrypt an items key, clients can use the `kp` (key params) included the items key's authenticated_data payload. These parameters represent the the key params of the root key used to encrypt this items key.
For example, when the account password changes, and thus the root key changes, all items keys are re-encrypted with the new root key on client A. Another client (client B) who may have a valid API session, but an outdated root key, will be able to download these new items keys. However, when client B attempts to decrypt these keys using its root key, the decryption will fail. Client B enters a state where it can save items to the server (wherein those items are encrypted using its existing default readable items key), but cannot read new data encrypted with items keys encrypted with client A's root key.
When client B connects to the API with a valid session token, but an outdated root key, it will be able to download new items keys, but not yet decrypt them. However, since the key parameters for the root key underlying the items key is included in the encrypted payload, the client will be able to prompt the user for their new password.
**In general,**
A. When a client encounters an items key it cannot decrypt, whose created date is greater than any existing items key it has, it will:
1. Make an authenticated request to the server to retrieve the account's current key parameters (because we suspect that they may have changed, due to the above fact). Authenticated requests to the GET key_params endpoint bypasses the MFA requirement.
2. Verify that the incoming key params version is greater than or equal to the client's current key params version. For example, if the client's key params version is 004, but the incoming key params version is 003, the client will reject these parameters as insecure and abort this process.
3. Prompt the user for their account password, including in the prompt its reason. i.e _"Your account password was changed 3 days ago. Enter your new account password."_
4. Validate the account password based on its root key's ability to decrypt the aforementioned items key. If it succeeds, replace the client's current root key with this new root key.
At this point, this client is now in sync. It does not need to communicate with the server to handle updating its state after a password change.
If the aforementioned items key's key params are not exactly equal to the server's key params (not a logical outcome, but assuming arbitrary desync), and no items keys exists with the same key params as the server key params, it must fallback to performing the regular sign in flow to authenticate its root key (based on its `serverPassword` field).
B. When a client encounters an items key it cannot decrypt, regardless of its created date, and the server key parameters are equal to the ones the client has on hand, this indicates that the items key may be encrypted with an older root key (for whatever reason).
In such cases, the client will present a "key recovery wizard", which all attempt to decrypt the stale items key:
1. Retrieve the key parameters associated with the authenticated_data of the items key's payload.
2. Prompt the user for their account password as it was on the date the key parameters were created. For example, _"Enter your account password as it was on Oct 20, 2019, 6:15AM."_
3. Generate a root key from the account password using the relevant key params, and use that root key to decrypt the stale items key. If the decryption is successful, the client will then decrypt any items associated with that items key. It will then mark the key as needing sync.
4. When the key subsequently runs through normal syncing logic, it will then proceed to be encrypted by the account's current root key, and synced to the account.
The above procedure represents a "corrective" course of action in the case that the sync following a root key change, where all items keys must be re-encrypted with the new root key, fails silently and results in inconsistent data.
Note that the difference between case A and case B is that in case A, we prompt the user for their account password and **update our client's root key** with the generated root key, if it is valid. In case B, we generate a temporary root key for decryption purposes only, but discard of the root key after our decryption. This distinction is important because in case A, the server will be required to return key parameters with version greater than or equal to the user's current version, but in case B, key parameters can be arbitrarily old. However, because in this case the root key is not used for anything other than transient read operations, we can accept protocol versions no matter how outdated they are.
### Expired Sessions
When a client encounters an invalid session network response (typically status code 498), it will:
1. Retrieve the latest key parameters from the server. (Note that because GETting key parameters may require MFA authentication, clients must be prepared to handle an "mfa-required" error response.)
2. Ensure the key parameter version is greater than or equal to the version the client currently has on hand.
3. Prompt the user for their account password, indicating the reason. i.e _"Your session has expired. Please re-enter your account password to restore access to your account."_
4. Proceed with normal sign in flow.
## Storage
**There exists three types of storage:**
1. **Value storage**—values such as user preferences, session token, and other app-specific values.
2. **Payload storage**—encrypted item payloads (such as notes and tags).
3. **Root key storage**—the primary root key.
How data is stored depends on different key scenarios.
### Scenario A
_No root key and no root key wrapper (no account and no passcode)_
- **Value storage**: Plain, unencrypted
- **Payload storage**: Plain, unencrypted
- **Root key storage**: Not applicable
### Scenario B
_Root key but no root key wrapper (account but no passcode):_
- **Value storage**: Encrypted with root key
- **Payload storage:** Encrypted with root key
- **Root key storage**:
- With device keychain: Plainly in secure keychain
- With no device keychain: Plainly in device storage
### Scenario C
_Root key and root key wrapper (account and passcode):_
- **Value storage**: Encrypted with root key
- **Payload storage**: Encrypted with root key
- **Root key storage**: Encrypted in device storage
### Scenario D
_No root key but root key wrapper (no account but passcode):_
- **Value storage**: Encrypted with root key wrapper
- **Payload storage**: Encrypted with root key wrapper
- **Root key storage**: Not applicable
## 003 Migration
For the most part, SNJS does not branch off into different modes of behavior for different protocol versions (apart from the version specific operators). This means that new constructs in 004, like items keys, are also used in 003. This is accomplished via migrations that are performed when the application detects older data state.
In particular, when SNJS detects a pre-existing 003 account (before the user even has the chance to perform the protocol upgrade), a migration will be triggered that creates a default `itemsKey` using the account's current `rootKey.masterKey`:
```
itemsKey = { itemsKey: rootKey.masterKey, version: '003' }
```
This `itemsKey` is encrypted as usual using `rootKey.masterKey`, and synced to the user's account. When the user eventually performs the 004 upgrade (by entering their account password when prompted), a new `itemsKey` will be created as a default for 004. However, their previously created 003 `itemsKey` will continue to exist, so that data previously encrypted with 003 will still be decryptable.
## Cryptography Specifics
**Key Derivation:**
| Name | Value |
| ------------------ | -------- |
| Algorithm | Argon2id |
| Memory (Bytes) | 67108864 |
| Iterations | 5 |
| Parallelism | 1 |
| Salt Length (Bits) | 128 |
| Output Key (Bits) | 512 |
**Encryption:**
| Name | Value |
| ------------------- | ------------------ |
| Algorithm | XChaCha20+Poly1305 |
| Key Length (Bits) | 256 |
| Nonce Length (Bits) | 192 |
### Root Key Derivation Flow - Specifics
Given a user `identifier` (email) and `password` (user password):
1. Generate a random salt `seed`, 256 bits (`hex`).
2. Generate `salt`:
1. `hash = SHA256Hex('identifier:seed')`
2. `salt = hash.substring(0, 32)`
3. Generate `derivedKey = argon2(password, salt, ITERATIONS, MEMORY, OUTPUT_LENGTH) `
4. Generate `rootKey` as:
```
{
masterKey: derivedKey.firstHalf,
serverPassword: derivedKey.secondHalf,
version: '004'
}
```
5. For account registration, `identifier`, `seed`, `serverPassword`, and `version` must be uploaded to the server.
**Understanding the salt `seed`:**
Our threat model is intended to distrust the server as much as possible. For this reason, we do not want to blindly trust whatever salt value a server returns to us. For example, a malicious server may attempt to mass-weaken user security by sending the same salt for every user account, and observe what interesting results the clients send back. Instead, clients play a more significant role in salt generation, and use the value the user inputs into the email field for salt generation.
At this point we have `salt = generateSalt(email)`. However, we'd ideally like to make this value more unique. Emails are globally unique, but well-known in advance. We could introduce more variability by also including the protocol version in salt computation, such as `salt = generateSalt(email, version)`, but this could also be well-accounted for in advance.
The salt `seed` serves as a way to make it truly impossible to know a salt for an account ahead of time, without first interacting with the server the account is hosted on. While retrieving a `seed` for a given account is a public, non-authorized operation, users who configure two-factor authentication can proceed to lock this operation so that a proper 2FA code is required to retrieve the salt `seed`. Salts are thus computed via `salt = generateSalt(email, seed)`.
### Items Key Generation Flow
1. Generate random `hex` string `key`, 256 bits.
2. Create `itemsKey = {itemsKey: key, version: '004'}`
### Encryption - Specifics
An encrypted payload consists of:
- `items_key_id`: The UUID of the `itemsKey` used to encrypt `enc_item_key`.
- `enc_item_key`: An encrypted protocol string joined by colons `:` of the following components:
- protocol version
- encryption nonce
- ciphertext
- authenticated_data
- `content`: An encrypted protocol string joined by colons `:` of the following components:
- protocol version
- encryption nonce
- ciphertext
- authenticated_data
**Procedure to encrypt an item (such as a note):**
1. Generate a random 256-bit key `item_key` (in `hex` format).
2. Encrypt `item.content` using `item_key` to form `content`, and `{ u: item.uuid, v: '004', kp: rootKey.key_params IF item.type == ItemsKey }` as `authenticated_data`, following the instructions _"Encrypting a string using the 004 scheme"_ below.
3. Encrypt `item_key` using the the default `itemsKey.itemsKey` to form `enc_item_key`, and `{ u: item.uuid, v: '004', kp: rootKey.key_params IF item.type == ItemsKey }` as `authenticated_data`, following the instructions _"Encrypting a string using the 004 scheme"_ below.
4. Generate an encrypted payload as:
```
{
items_key_id: itemsKey.uuid,
enc_item_key: enc_item_key,
content: content,
}
```
### Encrypting a string using the 004 scheme:
Given a `string_to_encrypt`, an `encryption_key`, `authenticated_data`, and an item's `uuid`:
1. Generate a random 192-bit string called `nonce`.
2. Encode `authenticated_data` as a base64 encoded json string (`base64(json(authenticated_data))`) where the embedded data is recursively sorted by key for stringification (i.e `{v: '2', 'u': '1'}` should be stringified as `{u: '1', 'v': '2'}`), to get `encoded_authenticated_data`.
3. Encrypt `string_to_encrypt` using `XChaCha20+Poly1305:Base64`, `encryption_key`, `nonce`, and `encoded_authenticated_data`:
```
ciphertext = XChaCha20Poly1305(string_to_encrypt, encryption_key, nonce, encoded_authenticated_data)
```
4. Generate the final result by combining components into a `:` separated string:
```
result = ['004', nonce, ciphertext, encoded_authenticated_data].join(':')
```
## Next Steps
Join the [Slack group](https://standardnotes.com/slack) to discuss implementation details and ask any questions you may have.
You can also email [help@standardnotes.org](mailto:help@standardnotes.org).
Follow [@standardnotes on Twitter](https://twitter.com/standardnotes) for updates and announcements.

View File

@@ -0,0 +1,118 @@
---
id: sync
title: Sync API
sidebar_label: Sync
description: Specification for the Standard Notes Sync API.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- sync
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
This document outlines the client-server communication of the Standard Notes ecosystem.
## Item
An `Item` is largely the only model that both clients and server are concerned wtih. The `content` field stores an encrypted object that can be anything the client needs to operate.
Relationships between items are handled by the client, and not the server, and are stored encrypted in the `content` field as `references`.
Items have the following properties:
| name | type | description |
| ------------ | ----------------------------------- | -------------------------------------------------------------------------------------------- |
| uuid | `string` (or uuid for some databases) | The unique identifier for this item. |
| content | `text` | An encrypted string generated by the client. |
| content_type | `string` | The type of data contained in the `content` field. (i.e Note, Tag, SN|Component, etc.) |
| enc_item_key | `text` | The key used to encrypt `content`, itself also encrypted. |
| deleted | `boolean` | Whether the model has been deleted. |
| created_at_timestamp | `integer` | Timestamp representing when the item was created, with microsecond precision. |
| updated_at_timestamp | `integer` | Timestamp representing when the item was updated _by the server_, with microsecond precision. |
## Content
Data generated by the client is stored in the `content` field of the `Item`. An item's `content` is "sticky" and can have any property the client chooses, as well as the following:
| name | type | description |
| ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| appData | `dictionary` | A domain based dictionary whose content must be preserved between platforms. `appData[domain]` contains data the client stores in the item. |
| references | `array` | A metadata array of other `Item` uuids this item is related to and their respective `content_type`. See sample below. |
`references` array structure:
```JSON
[
{uuid: xxxx, content_type: "Tag"},
{uuid: xxxxx, content_type: "Tag"},
]
```
## REST API
General:
1. All requests after initial authentication should be authenticated with a session token in the `Authorization` header:
```
Authorization: Bearer <access token>
```
### POST items/sync
| | | |
|-------------------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| api | `string` | The API version to use. Must not be null, otherwise oldest API version used. Latest value is "20200115". |
| compute_integrity | `boolean` | Should be false in most cases. Pass true if you plan to read the `integrity_hash` result (integrity can be optionally checked once on app launch). |
| items | `array` | An array of Items. |
| limit | `integer` | The maximum number of items to return per request. Official clients use 150. |
| sync_token | `string` | An opaque token returned by the server from a previous sync request. This lets the server know to pull in only new changes. |
| cursor_token | `string` | An opaque pagination token returned by the server from a previous multi-page sync request. Send this value back only if the server returned this value from a previous request. |
#### Response
```JSON
{
"retrieved_items": Array<Item>,
"saved_items": Array<Item>,
"conflicts": Array<Conflict>,
"sync_token": string,
"cursor_token": string?,
"integrity_hash": string?,
}
```
| | |
|-----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| retrieved_items | Items that are new to the client or have been modified since last sync and should be merged with local values or created locally. |
| saved_items | Items which have been successfully saved. Clients should merge *only* the resulting `updated_at_timestamp` with local values. Do not update local value of `content` from remote `saved_items` value. |
| conflicts | Items which have not been saved by the server and returned to client. |
| sync_token | An opaque token which should be sent back to server in subsequent sync request. |
| cursor_token | An opaque token which should be sent back to server in subsequent sync request (if present). |
#### Conflicts
```typescript
type Conflict = {
server_item?: Item
unsaved_item?: Item
type: 'sync_conflict' | 'uuid_conflict'
}
```
A `sync_conflict` occurs when the item the client is attempting to save has a newer change on the server. The server is able to determine this by reading the `updated_at_timestamp` value from both the incoming payload and the server payload. If the incoming payload's `updated_at_timestamp` != to the server's `updated_at_timestamp` for that item, the save is rejected and returned to client as a `sync_conflict`. These types of conflicts are not uncommon.
To resolve a `sync_conflict`, the client must choose a winner between server_item and unsaved_item then re-sync. The winning item should have its updated_at_timestamp value set to the server's value.
A `uuid_conflict` occurs when the UUID the client has choosen for an item for X user already belongs to an item for Y user. This should mostly only occur when a user registers a new account and imports a data backup from another account.
To resolve a `uuid_conflict`, change the UUID of the item attempting to be saved to a new UUID, then re-sync.
#### Deletion
To delete an item permanently, set `deleted` to `true` on the `Item` and sync. When receiving an item that is `deleted`, remove it from the local database immediately.

View File

@@ -0,0 +1,16 @@
---
slug: template/
id: template
title: Template
sidebar_label: Template
description: Template
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- Template
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---

View File

@@ -0,0 +1,56 @@
---
slug: import-backups
id: import-backups
title: How to import a backup without being signed in
sidebar_label: Import backups
description: How to import a backup in the Standard Notes web and desktop app without being signed in.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- web
- desktop
- import backups
- backups
- import
- data
- account
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
We are aware of issues with importing backups belonging to another account when that account is still registered, such as when you are [changing your account email](https://standardnotes.com/help/7/how-can-i-change-my-account-email). If you are signed in to your new account, some tags are duplicated and are not properly assigned to notes and a significant number of notes, tags and/or editors are not imported. We are working on a fix. As a temporary workaround, you can import the backup while signed out of the app before signing in to the new account. Then, when you sign in, choose to merge local data (this is an option that is on by default).
:::note
For the best experience, use a backup that was exported from the [web](https://app.standardnotes.org) or desktop app. A backup that was generated from [CloudLink](https://standardnotes.com/help/27/how-do-i-enable-dropbox-google-drive-or-onedrive-backups) may not work as well.
:::
## Clear your account
If you are trying to change your sync account email from an old email to a new email and tried to import a backup but encountered the issues described above, then you probably want to clear your **new** account before reimporting the backup. Please **do not** clear your **old** account until your backup has been successfully imported in your new account.
There are two ways to clear your account: delete everything using the free Batch Manager extension or use the [reset tool](https://standardnotes.com/reset). The reset tool ensures that your new account is completely empty before you reimport your backup.
## Reimport backup
After clearing your new account, please download a backup from your old account and import it into your new account while merging local data:
1. Open the [web app](https://app.standardnotes.org) or desktop app.
2. Export an encrypted or decrypted backup from your old account via the **Account** menu at the bottom left corner of the app.
3. After you have an export downloaded on your computer, click **Sign Out**, then click **Clear session data**.
4. Click on the **Account** menu. Without signing in, click on **Import Backup** to import your backup into the app. If you are importing an encrypted backup, enter your previous password when prompted.
5. Sign in to your new account with Merge local data **on** (this is on by default).
a. If you cleared your new account using the Batch Manager, then you can sign in to your new account. Click on **Sign In**, enter your account's credentials, and make sure the **Merge local data** option is selected before clicking on **Sign In** again.
b. If you cleared your account using the [reset tool](https://standardnotes.com/reset), then you need to register your new account again. Click on **Register**, enter your email and password, confirm the password, and make sure the **Merge local data** option is selected before clicking on **Register** again.
6. After signing in, verify that all your notes, tags, and other items were properly imported into your account.

View File

@@ -0,0 +1,82 @@
---
slug: reset-apps
id: reset-apps
title: How do I reset the app?
sidebar_label: Reset the Apps
description: How to reset the Standard Notes app on each platform.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- reset apps
- delete
- clear storage
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
If you are experiencing issues with signing in to the apps, you may want to clear the apps storage. Before proceeding, please sign in to the [web](https://app.standardnotes.org) app (in a different browser, if needed) to confirm that notes, tags, and other items on your notes syncing account that were modified or created on another device were saved and synced to our servers.
:::warning
If you do not have an account, do not proceed with these steps until a fix has been made available. You may have some luck in simply entering in your password or passcode several times until the app unlocks.
:::
## Mobile
**Please note:** Reinstalling the app on mobile devices may download a backup of your app from the cloud and you might run into the same issue again. Clearing the cache alone is usually not enough to reset the app. If you have an [account](https://app.standardnotes.org) with us and can verify that your changes have synced, then clearing the app's storage will not result in the loss of your notes.
### [Android](https://play.google.com/store/apps/details?id=com.standardnotes&hl=en_US)
1. Locate the Standard Notes app in the app drawer.
2. Press and hold the apps icon until a small menu appears.
3. Tap on **App Info**.
4. Locate and use the option to delete/clear the apps storage (and cache).
5. Relaunch the app and sign in again.
**Please note:** Do not reinstall the app afterwards.
### [iOS](https://apps.apple.com/us/app/standard-notes/id1285392450)
1. Please open the **Settings** app, and navigate to your iCloud backups with the following path:
**[Your name and iCloud account username (at the top)] → iCloud → Manage Storage → Backups → [Tap on the correct device]**
2. Wait for the list of apps to load.
- Tap on “Load all/more apps” if necessary and wait for the list to expand once more
3. Finally, locate the Standard Notes app and tap on the toggle button to remove the current and all future backups of the app from iCloud.
4. From there, please reinstall the app and sign in again.
- You can enable the setting to backup the app to iCloud again, if you would like.
## [Desktop](https://standardnotes.com/download) and [Web](https://app.standardnotes.org)
The methods for clearing the storage of the [desktop](https://standardnotes.com/download) and [web](https://app.standardnotes.org) apps are largely similar but require toggling the developer tools, which requires different sets of steps.
### Desktop
1. Click on the menu button/bar.
2. Click on the **View** dropdown menu.
3. Click on the **Toggle Developer Tools** option.
4. Expand it until it reveals a button for tab named **Application**.
5. Click on **Clear Storage**.
6. Click on **Clear site data**.
7. Sign in again.
### Web
#### Chromium-based browsers, such as Google Chrome, Vivaldi, and Microsoft Edge
1. Right-click anywhere on the web app.
2. Click on **Inspect Element** in the context menu.
3. Expand the Developer Tools view that appears until it reveals a button for tab named **Application**.
4. Click on **Clear Storage**.
5. Click on **Clear site data**.
6. Sign in again.
#### Firefox-based browsers
1. Right-click anywhere on the web app.
2. Click on **Inspect Element** in the context menu.
3. Click on the **Storage tab**.
4. Expand each item on the left side.
5. Check on all of the items at the lowest levels by clicking on each one, and then click on the items that appear on the right (if there are any) and delete them.
6. Sign in again.

View File

@@ -0,0 +1,63 @@
---
title: Action Bar
sidebar_label: Action Bar
description: How to use the Standard Notes Action Bar.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- action bar
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
The Action Bar is a utility bar for Standard Notes. It provides information about the current note and a few useful actions.
## Information
The Action Bar provides the following information:
- The title of the note
- The date and time when the note was created
- The date and time when the note was last updated
- The approximate number of words in the note (as determined by the spaces)
- The approximate number of paragraphs in the note (as determined by line skips)
- The number of characters in the note
- The approximate read time of the note in minutes (approximated with a read speed of 200 words per minute)
## Actions
The Action Bar provides the following actions:
- Copy the current date to your clipboard in the format `Month/Date/Year, Hour:Minute AM/PM`
- Duplicate the note
- Copy the contents of the note to your clipboard
- Save the contents of the note to a file.
- Email the note. This creates and clicks a `mailto:` link with the note's title as the subject of the email and the note's content as the body of the email.
:::tip
The default extension for saving a note is `.txt`. You can also save your files with `.md`, `.html`, and `.tex` extensions. If you frequently export a note with a particular file extension, you can add the extension to the title of the note. For example, naming your note `My Blog Post.md` or `Book.tex` will export the notes as `.md` and `.tex` files, respectively.
:::
## Development
The Action Bar is written in JavaScript and compiled with Grunt.
1. Clone the [action-bar](https://github.com/standardnotes/action-bar) repository from GitHub.
2. Run `npm install` to install required dependencies.
3. Ensure that either the Standard Notes desktop app is available for use or the web app is accessible. Use both locally or with an Extended account (or the extension will not load).
4. Follow the instructions [here](/extensions/local-setup) to setup the extension locally.
- For the `area` property, use `editor-stack`
5. Begin development! Upon making any changes to the code, run `grunt` to build the files to the `dist` folder.
## License
The Action Bar is licensed under the GNU [AGPL-3.0-or-later](https://github.com/standardnotes/action-bar/blob/master/LICENSE).
## Resources
- [GitHub](https://github.com/standardnotes/action-bar)

View File

@@ -0,0 +1,28 @@
---
title: Autocomplete Tags
sidebar_label: Autocomplete Tags
description: How to use the Standard Notes quick tags extension.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- Template
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
Work more efficiently by quickly selecting from a live list of tags while you type. Supports keyboard shortcuts and folders.
## Development
The Autocomplete Tags component is written in JavaScript is compiled with Grunt.
## License
The Autocomplete Tags component is licensed under the GNU [AGPL-3.0 or Later](https://github.com/sn-extensions/autocomplete-tags/blob/master/LICENSE)
## Resources
- [GitHub](https://github.com/sn-extensions/autocomplete-tags)

View File

@@ -0,0 +1,67 @@
---
id: 'bold-editor'
title: Bold Editor
sidebar_label: Bold Editor
description: How to use the Standard Notes bold editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- bold editor
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
export const Highlight = ({children, color}) => ( <span style={{
backgroundColor: color,
borderRadius: '2px',
color: '#fff',
padding: '0.2rem',
}}> {children} </span>
//#28a745 green
//#1877F2 blue
//#ffc107 yellow
//#dc3545 red
);
## Introduction
The Bold Editor is a [derived editor](https://standardnotes.com/help/77/what-are-editors) for Standard Notes. It is derived from the [Redactor](https://imperavi.com/redactor). The instructions for adding in-line images, videos, and audio recordings are available [here](https://standardnotes.com/help/71/how-do-i-add-images-to-my-notes).
### <Highlight color="#1877F2">Warning</Highlight>
Changing the editor for a note to the Bold Editor will add `html` tags around each line of your text. These tags will be present when you change the editor back to a markdown editor.
If you want to convert a note from HTML to plaintext, you will need to remove these tags manually or by using a separate text editor such as [VS Code](https://code.visualstudio.com/) or [Atom](https://atom.io) because we do not yet have a "find and replace" feature. If you would like to test the Bold Editor with your note, you can restore a previous copy of the note in the Session History. If you restore an old copy, then any changes made with the Bold Editor will be lost.
The search feature in the [Minimist editor](https://standardnotes.com/extensions/markdown-minimist) may help you remove the tags manually.
## Keyboard Shortcuts
| Result | Shortcut |
| :----------------- | :------------------------------- |
| Remove format | Ctrl/⌘ + m |
| Undo | Ctrl/⌘ + z |
| Redo | Ctrl/⌘ + y or Shift + Ctrl/⌘ + z |
| Bold | Ctrl/⌘ + b |
| Italic | Ctrl/⌘ + i |
| Superscript | Ctrl/⌘ + h |
| Subscript | Ctrl/⌘ + l |
| Link | Ctrl/⌘ + k |
| Ordered List | Ctrl/⌘ + Shift + 7 |
| Unordered List | Ctrl/⌘ + Shift + 8 |
| Outdent | Ctrl/⌘ + [ |
| Indent | Ctrl/⌘ + ] |
| Normal (Pagagraph) | Ctrl/⌘ + Alt + 0 |
| Heading 1 | Ctrl/⌘ + Alt + 1 |
| Heading 2 | Ctrl/⌘ + Alt + 2 |
| Heading 3 | Ctrl/⌘ + Alt + 3 |
| Heading 4 | Ctrl/⌘ + Alt + 4 |
| Heading 5 | Ctrl/⌘ + Alt + 5 |
| Heading 6 | Ctrl/⌘ + Alt + 6 |
## Further Resources
- [GitHub](https://github.com/standardnotes/bold-editor) - Development instructions, license (AGPL-3.0-or-later), and source code.

View File

@@ -0,0 +1,15 @@
---
id: 'code-editor'
title: Code Editor
sidebar_label: Code Editor
description: How to use the Standard Notes code editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- code editor
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---

View File

@@ -0,0 +1,56 @@
---
slug: code-pro
id: code-pro
title: Code Pro Editor (Beta)
sidebar_label: Code Pro Editor
description: How to use the Standard Notes Code Pro Editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- Code Pro Editor
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
Code Pro is a [derived editor](https://standardnotes.com/help/77/what-are-editors) for [Standard Notes](https://standardnotes.com), a free, [open-source](https://standardnotes.com/knowledge/5/what-is-free-and-open-source-software), and [end-to-end encrypted](https://standardnotes.com/knowledge/2/what-is-end-to-end-encryption) notes app.
Code Pro is a code editor powered by the [Monaco Editor](https://microsoft.github.io/monaco-editor/) (Visual Studio Code). It is meant for writing Markdown and 60 other programming languages.
Code Pro is not meant to be used on mobile devices.
## Features
- Syntax highlighting for Markdown and more than 60 other programming languages
- Languages supported: abap, aes, apex, azcli, bat, c, cameligo, clojure, coffeescript, cpp, csharp, csp, css, dart, dockerfile, fsharp, go, graphql, handlebars, hcl, html, ini, java, javascript, json, julia, kotlin, less, lexon, lua, markdown, mips, msdax, mysql, objective-c, pascal, pascaligo, perl, pgsql, php, plaintext, postiats, powerquery, powershell, pug, python, r, razor, redis, redshift, restructuredtext, ruby, rust, sb, scala, scheme, scss, shell, sol, sql, st, swift, systemverilog, tcl, twig, typescript, vb, verilog, xml, yaml
- Autocompletion
- Intelligent autocompletion for CSS, JavaScript, JSON, Less, Handlebars, HTML, Razor, SCSS, and TypeScript
- Sophisticated search and replace
- Prettier formatting for CSS, GraphQL, Markdown, HTML, JavaScript, Less, TypeScript, Sass, and Yaml. Built-in formatting for JSON.
- Settings: language, font size, tab size (`2` or `4`), theme (light, dark, high contrast, or SN themed), and word wrap (`on`, `off`, and `bounded`)
- Per-note settings
- Buttons to save and load default settings
## Keyboard Shortcuts
Perform these shortcuts with the editor
| Action | Shortcut |
| :-------------------------------------------------------------- | :----------------------------------------------- |
| Toggle word wrap between `on` and `off` (bounded is unaffected) | <kbd>Alt</kbd> + <kbd>Z</kbd> |
| Format code with Prettier^ | <kbd>Shift</kbd> + <kbd>Alt</kbd> + <kbd>F</kbd> |
| Toggle Tab Key Moves Focus (vs tab spacing) | <kbd>Ctrl/⌘</kbd> + <kbd>M</kbd> |
^ For CSS, GraphQL, Markdown, HTML, JavaScript, Less, TypeScript, Sass, and Yaml. Some languages, such as JSON, have built-in formatters.
Each time the editor refreshes (e.g., toggling word wrap, formatting code), the editor remembers your position (line number and column) and centers it on the screen if it's not already in focus.
## Settings
The settings for each note are saved automatically after they are changed. Loading default settings will sync the note's settings with the default settings and save automatically.
### Themes
The Monaco Editor comes with three themes: `vs` (a white/light theme), `vs-dark` (a dark theme like the default theme for VS Code), and `hc-black` (a high contrast dark theme). There is also one more option: `sn-theme`. The `sn-theme` option takes either `vs` or `vs-dark` depending on your system theme and adjusts some of the colors (e.g., link colors) to match the theme. The `sn-theme` is still a work-in-progress.

View File

@@ -0,0 +1,138 @@
---
slug: filesafe/aws
id: filesafe-aws
title: FileSafe with Amazon S3
sidebar_label: Amazon S3
description: How to use Amazon S3 with Standard Notes FileSafe.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- FileSafe
- AWS Amazon S3
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
This guide will help you set up your AWS account with an S3 bucket and User to be used by the Standard Notes FileSafe extension.
## Log In
Start by logging into your [AWS account](https://console.aws.amazon.com). Make sure to pick your preferred region in the top right corner.
![image](/img/filesafe/aws/preferred-region.png)
Check the [list of regions](https://docs.aws.amazon.com/general/latest/gr/rande.html) and take note of the region code you picked. In the example above I picked **Paris** so my region code is **eu-west-3**.
## Create the S3 bucket
In the Services dropdown of the AWS console pick the **S3 storage service** and click on the **Create bucket** button.
Choose a name for your bucket and select the region you chose before.
![image](/img/filesafe/aws/create-bucket.png)
You can **skip directly to step 3** if you don't want any additional features such as _versioning_ or _logging_ for your bucket.
In **step 3** make sure to keep **_Block all public access_** selected.
After confirming your settings you should see your new bucket.
![image](/img/filesafe/aws/block-all-public-access.png)
## Create the IAM user with the required permissions
We'll start by creating the read/write policy for the new bucket, then, we'll create a group with that policy and finally create our user and assign it to our group.
## Create the policy
In the services dropdown select **IAM** and go to **Policies.** Click on the **Create policy** button and you should see the following screen:
![image](/img/filesafe/aws/create-policy.png)
Now click on the JSON tab and add the following policy configuration:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": ["arn:aws:s3:::<bucket-name>"]
},
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
"Resource": ["arn:aws:s3:::<bucket-name>/*"]
}
]
}
```
Make sure to replace **`<bucket-name>`** with the name of the bucket you created in the previous step.
Click on the **Review policy** button, pick the name for your policy and create the policy.
![image](/img/filesafe/aws/review-policy.png)
## Create the group
Back on the **IAM** console pick **Groups** from the side menu and click on **Create New Group.**
Choose your group name, click N**ext Step** and pick the policy you created previously.
![image](/img/filesafe/aws/create-group.png)
![image](/img/filesafe/aws/attach-policy-to-group.png)
After reviewing your configuration create the group.
![image](/img/filesafe/aws/review-group.png)
## Creating the user
Back on the **IAM** console pick **Users** from the side menu and click on **Add user**.
Choose a user name and make sure to select **Programmatic access.**
![image](/img/filesafe/aws/create-user.png)
On the next screen add our user to the group we just created.
![image](/img/filesafe/aws/add-user-to-group.png)
You can skip the tags screen and create the user.
In the success screen make sure to either download the **CSV** or copy the **Access key ID** and **Secret access key** as you won't be able to view the secret access key in the future.
![image](/img/filesafe/aws/copy-access-key.png)
## Standard Notes
In **Standard Notes** pick **Add New** from the **Integrations** section in **FileSafe**.
![image](/img/filesafe/aws/add-integration-in-sn.png)
In the **Link Integrations** page pick the **AWS S3** option and fill all the required information.
![image](/img/filesafe/aws/link-integrations.png)
![image](/img/filesafe/aws/submit-integration.png)
Copy the code generated to **Standard Notes** and you should see the **AWS S3** integration in the app.
![image](/img/filesafe/aws/test-integration-1.png)
### Testing the integration
Simply attach a file to your note to test the integration. The file should be available in **Standard Notes** and in your **S3 bucket** under **FileSafe**:
![image](/img/filesafe/aws/test-integration-2.png)
![image](/img/filesafe/aws/view-test-integration-in-aws.png)
Congratulations! **FileSafe** is now integrated with your **S3 bucket**.

View File

@@ -0,0 +1,55 @@
---
title: Folders
sidebar_label: Folders
description: How to use the Standard Notes folders extension.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- Template
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
You can use the [Folders](https://standardnotes.com/extensions/folders) extension to create nested folders from your tags with easy drag and drop. Folders also supports [Smart Tags](/usage/tags), which allow you to build custom filters for viewing your notes.
## Terminology
The words _tags_ and _folders_ can be used interchangeably in the Standard Notes context. However, when discussing their use on the web and desktop apps, it is helpful to distinguish them. The labels located at the top of the app are referred to as _tags_ and the labels located on the left side of the app are referred to as _folders_.
When tags are nested inside other tags, the outer/higher level tag is called a _parent_ tag and the inner/lower level tag is called a _child_ tag.
Nested tags are recorded in a `Parent.Child` format and appear that way on mobile.
## Usage
If you add a child tag to a note, the tag's parent tag may or may not be added depending on how you add the child tag.
If you create a note in a folder (e.g., the **All** folder) and add a child tag using the [Quick Tags](/usage/tags) extension, its parent tag will also be added. If you create a note in the child folder, the child tag will automatically be added to the note, but the tag for the parent folder will not be added.
For example, if your **Recipes** folder includes the **Cakes** and **Pasta** tags, then there are two main ways to approach adding "Spaghetti Recipe" note to the **Pasta** folder:
- Create a note in the **Pasta** folder. One tag will be added: **Recipes.Pasta**.
- Create the note a folder other than the **Pasta** folder and type "Pasta" into the top input field for tags. Two tags will be added: **Recipes** and **Recipes.Pasta**.
:::note
You may need to refresh your app after deleting a folder/tag.
:::
## Development
The Folders Component is written in JavaScript and compiled with Grunt.
## License
The source code for the Folders Component is licensed under GNU [AGPL-3.0-or-later](https://github.com/standardnotes/folders-component/blob/master/LICENSE).
## Resources
- [GitHub](https://github.com/standardnotes/folders-component)

View File

@@ -0,0 +1,79 @@
---
slug: /usage
id: general
title: General Usage
sidebar_label: General Usage
description: How to use the Standard Notes app.
keywords:
- standard notes
- notes app
- end-to-end encryption
- usage
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Keyboard Shortcuts
### Edit
| Result | Windows/Linux | Mac |
| :-------------------------------- | :--------------- | :------------------ |
| Undo | Ctrl + Z | ⌘ + Z |
| Redo | Ctrl + Y | ⌘ + Y |
| Cut | Ctrl + X | ⌘ + X |
| Copy | Ctrl + C | ⌘ + C |
| Paste | Ctrl + V | ⌘ + V |
| Paste and Match Style\* | Ctrl + Shift + V | ⌘ + Shift + V |
| Select All | Ctrl + A | ⌘ + A |
| Jump to the beginning of the note | Ctrl + Home | ⌘ + Home or ⌘ + Up |
| Jump to the end of the note | Ctrl + End | ⌘ + End or ⌘ + Down |
\*Paste and Match Style only works with Rich Text Editors such as the Bold and Plus editors
### View
| Result | Windows/Linux | Mac |
| :--------------------- | :------------------------ | :--------------------- |
| Reload | Ctrl + R | ⌘ + R |
| Toggle Developer Tools | Ctrl + Shift + I | ⌘ + Shift + I |
| Actual Size | Ctrl + 0 | ⌘ + 0 |
| Zoom In | Ctrl + Shift + `+` (plus) | ⌘ + Shift + `+` (plus) |
| Zoom Out | Ctrl + `-` (minus) | ⌘ + `-` (minus) |
| Toggle Full Screen | F11 | F11 |
| Hide Menu Bar\* | Alt + M | Alt + M |
\*The Hide Menu Bar shortcut is available only when not using the Themed Menu Bar (window decoration). To toggle the Themed Menu Bar on Windows and Linux, visit **☰** > View > Themed Menu Bar.
### Window
| Result | Windows/Linux | Mac |
| :------- | :------------ | :---- |
| Minimize | Ctrl + M | ⌘ + M |
| Close | Ctrl + W | ⌘ + W |
### Reserved
These keyboard shortcuts have not been implemented but are reserved for future use. Developers who are interested in implementing keyboard shortcuts for their extensions should avoid using these shortcuts.
- Ctrl/⌘ + `T`
- Ctrl/⌘ + Shift + `F`
- Ctrl/⌘ + Shift + `L`
## Note Options
What does it mean to lock or protect a note?
<!-- Copied from https://standardnotes.com/help/54/what-does-it-mean-to-lock-or-protect-a-note -->
Here is a list of available options on a note and what they represent:
| Option | Description |
| :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Pin** | Pinning a note will anchor it to the top of your list of notes, sorted according to the global sort order (specified in your _Options_). |
| **Archive** | Archiving a note will stash your note and hide it from your usual interface. Archived notes can be found by choosing the reserved _Archived_ view in the tags panel. Archiving a note does not affect or improve performance, as the note is still saved and loaded, but not displayed in the list of all notes or within a particular tag. Archiving is useful for notes that no longer contain actionable data, but want to be preserved for historical purposes. For example, if a note contains a list of todos, and you've completed all the todos, archiving the note would make sense. |
| **Lock** | Locking a note will put it read-only mode, which means it can't be edited or deleted until you unlock it. This is to prevent accidental modification of sensitive notes, like passwords or credentials that aren't likely to change often. |
| **Protect** | Protecting a note marks the note as sensitive, and makes it so that additional authentication is required to view the note. The form of authentication used depends on your configuration. You will be asked to authenticate with your passcode or biometrics (mobile) if you have that configured. If you do not have a passcode or biometrics configured, you will be asked to authenticate with your account password. If neither of those are configured and there is no protection source available, the protected action will proceed without authentication. In addition, protecting a note will automatically hide its preview in your list of notes. |
| **Preview** | On web and desktop, selecting the Preview option will toggle whether to hide or show the note preview in the list of notes. You can hide previews for a particular note if you'd like to conserve vertical space in your list of notes, or to hide sensitive data. However, we recommend _Protecting_ a note if you'd like to hide sensitive information. |
| **Move to Trash** | Moving a note to the trash will mark the note as deleted, and remove it from your interface. However, your note will still exist in your Trash, which you can access from your list of _Views_. From the trash, you can restore the note, or choose to delete it permanently. You can also empty the entirety of your trash by choosing **Options → Empty Trash** from the note editor panel. |

View File

@@ -0,0 +1,30 @@
---
slug: github-push
id: github-push
title: GitHub Push
sidebar_label: GitHub Push
description: How to use the Standard Notes GitHub Push extension.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- github push
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
The GitHub Push Action Extension pushes a note to a public or private repository. You can choose a directory, extension and commit message, as well as which repository to push to.
Required permissions for public repo access:
- public_repo
![Public access](https://user-images.githubusercontent.com/772937/92886567-9a8a8700-f3c8-11ea-9560-b1956eecdf4b.png)
Required permissions for private repo access:
- repo
![Private access](https://user-images.githubusercontent.com/772937/92886571-9bbbb400-f3c8-11ea-86d8-713f0041a5ba.png)

View File

@@ -0,0 +1,122 @@
---
id: 'markdown-basic'
title: Markdown Basic Editor
sidebar_label: Markdown Basic
description: How to use the Standard Notes Markdown Basic editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- markdown basic
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
Markdown Basic is a [custom editor](https://standardnotes.com/help/77/what-are-editors) for Standard Notes. It uses Markdown-It to parse Markdown.
## Features
- Markdown via Markdown-It
- Syntax Highlighting via Highlight.js
- Optional split pane view
- Task Lists
- Tables
- Footnotes
- Inline external images
## Installation
1. Register for an account at Standard Notes using the [Desktop App](https://standardnotes.com/download) or [Web app](https://app.standardnotes.org). Remember to use a strong and memorable password.
2. Sign up for [Standard Notes Extended](https://dashboard.standardnotes.com/member). Then, follow the instructions [here](https://standardnotes.com/help/29/how-do-i-install-extensions-once-i-ve-signed-up-for-extended) or continue.
3. Click **Extensions** in the lower left corner.
4. Under **Repository**, find **Markdown Basic**.
5. Click **Install**.
6. Close the **Extensions** pop-up.
7. At the top of your note, click **Editor**, then click **Markdown Basic**.
8. Click **Continue**.
After you have installed the editor on the web or desktop app, it will automatically sync to your [mobile app](https://standardnotes.com/download) after you sign in.
## Style Guide
| Result | Markdown |
| :----------------- | :------------------------------------------- |
| **Bold** | \*\*text\*\* or \_\_text\_\_ |
| _Emphasize_ | \*text\* or \_text\_ |
| ~~Strike-through~~ | \~\~text\~\~ |
| Link | [text]\(http://) |
| Image | ![text]\(http://) |
| `Inline Code` | \`code\` |
| Code Block | \`\`\`language <br></br>code <br></br>\`\`\` |
| Unordered List | \* item <br></br> - item <br></br> + item |
| Ordered List | 1. item |
| Task List | `- [ ] Task` or `- [x] Task` |
| Blockquote | \> quote |
| H1 | # Heading |
| H2 | ## Heading |
| H3 | ### Heading |
| H4 | #### Heading |
| Section Breaks | `---` or `***` |
## Tables
Colons can be used to align columns.
Copy this into your editor to see what it renders:
```
| Tables | Are | Cool |
| ---------------- | :-----------: | --------: |
| col 2 is | centered | \$149 |
| col 3 is | right-aligned | \$4.17 |
| privacy is | neat | \$2.48 |
| rows don't need to |be pretty| what? |
| the last line is | unnecessary | really?
| one more | row | Yay! 😆
```
## Footnotes
The Markdown Basic editor supports footnotes. The footnote links do not work properly on mobile. Copy this into your note to see how they're used:
```md
You can create footnote references that are short[^1] or long.[^2]
You can also create them inline.^[which may be easier,
since you don't need to pick an identifier and move down to type the note]
The footnotes are automatically numbered at the bottom of your note,
but you'll need to manually number your superscripts.
Make sure to count your variable[^variable] footnotes.[^5]
[^1]: Here's a footnote.
[^2]: Heres a footnote with multiple blocks.
Subsequent paragraphs are indented to show that they belong to the previous footnote.
{ eight spaces for some code }
The whole paragraph can be indented, or just the first
line. In this way, multi-paragraph footnotes work like
multi-paragraph list items.
This paragraph wont be part of the footnote, because it
isnt indented.
[^variable]: The variable footnote is the fourth footnote.
[^5]: This is the fifth footnote.
```
#### Not yet available:
- KaTeX
- Printing
- Custom Font Families
- Custom Font Sizes
- Superscript
- Subscript
## Further Resources
- [GitHub](https://github.com/sn-extensions/markdown-basic/) - Development instructions, license (AGPL-3.0-or-later), and source code.

View File

@@ -0,0 +1,98 @@
---
id: 'markdown-math'
title: Markdown Math
sidebar_label: Markdown Math
description: How to use the Standard Notes Markdown Math editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- markdown math
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
The Markdown Math editor (aka Math Editor) is a [derived editor](https://standardnotes.com/help/77/what-are-editors) for Standard Notes. It is derived from the [Upmath](https://github.com/parpalak/upmath.me) editor by [Roman Parpalak](https://github.com/parpalak), but uses [KaTeX](https://katex.org) for client-side rendering. Because the original Upmath editor and the Markdown Math editor render math using slightly different methods, some TeX libraries and their environments may be available in the Upmath editor but not in the Markdown Math editor. For a full list of functions supported by KaTeX, please see the [official KaTeX documentation](https://katex.org/docs/supported.html).
## Features
- $\LaTeX$ math rendering via [$\KaTeX$](https://katex.org)
- Markdown with side-by-side live rendering
- Option to view the HTML source of the rendered markdown
- Option to overwrite the note text with the contents of a text file on local storage
- Option to download the plain note text as a text file to save on local storage
- Option to download the HTML source of the rendered text as a text file to save on local storage
## Style Guide
| Result | Markdown |
| :----------------- | :------------------------------------------- |
| **Bold** | \*\*text\*\* or \_\_text\_\_ |
| _Emphasize_ | \*text\* or \_text\_ |
| ~~Strike-through~~ | \~\~text\~\~ |
| Link | [text]\(http://) |
| Image | ![text]\(http://) |
| `Inline Code` | \`code\` |
| Code Block | \`\`\`language <br></br>code <br></br>\`\`\` |
| Unordered List | \* item <br></br> - item <br></br> + item |
| Ordered List | 1. item |
| Blockquote | \> quote |
| H1 | # Heading |
| H2 | ## Heading |
| H3 | ### Heading |
| H4 | #### Heading |
| Section Breaks | `---` or `***` |
## Tables
Colons can be used to align columns.
Copy this into your editor to see what it creates:
```
| Tables | Are | Cool |
| ---------------- | :-----------: | --------: |
| col 2 is | centered | \$149 |
| col 3 is | right-aligned | \$4.17 |
| privacy is | neat | \$2.48 |
| rows don't need to |be pretty| what? |
| the last line is | unnecessary | really?
| one more | row | Yay! 😆
```
## $\KaTeX$
The Markdown Math editor requires double dollar signs. For example, `$$\int_0^\infty f(x)dx$$` or `$$\pi$$` should yield $$\int_0^\infty f(x)dx$$ and $$\pi$$.
To use Display Mode in the KaTeX, use double dollar signs on new lines. For example,
```latex
Text
$$
\int_0^\infty f(x)dx
$$
More Text
```
should yield:
Text
$$
\int_0^\infty f(x)dx
$$
More Text
### $\KaTeX$ Tables
Please see [here](https://katex.org/docs/supported.html) and [here](https://katex.org/docs/support_table.html) for tables of all the functions and symbols that $\KaTeX$ supports.
## Further Resources
- [GitHub](https://github.com/sn-extensions/math-editor) - Development instructions, license (AGPL-3.0-or-later), and source code.

View File

@@ -0,0 +1,14 @@
---
id: 'markdown-minimist'
title: Markdown Minimist Editor
sidebar_label: Markdown Minimist
description: How to use the Standard Notes Markdown Minimist editor.
- standard notes
- docs
- notes app
- end-to-end encryption
- markdown minimist
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---

View File

@@ -0,0 +1,125 @@
---
id: 'markdown-pro'
title: Markdown Pro
sidebar_label: Markdown Pro
description: How to use the Standard Notes Markdown Pro editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- markdown pro
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
The Markdown Pro editor (aka Advanced Markdown Editor) is a [derived editor](https://standardnotes.com/help/77/what-are-editors) for Standard Notes. It is derived from the [Easy Markdown Editor](https://github.com/Ionaru/easy-markdown-editor) which uses [Codemirror](https://github.com/codemirror/codemirror).
## Features
- Markdown with live side-by-side rendering
- Three views: Edit, Split, and Preview
- Keyboard Shortcuts
- Inline styling with HTML/CSS
## Keyboard Shortcuts
| Result | Shortcut |
| :------------------ | :--------------- |
| Toggle Preview | Ctrl/⌘ + P |
| Toggle Side-by-Side | Ctrl/⌘ + Alt + P |
## Style Guide
| Result | Markdown | Shortcut |
| :----------------- | :---------------------------------------- | :---------------------------------- |
| **Bold** | \*\*text\*\* or \_\_text\_\_ | Ctrl/⌘ + B |
| _Emphasize_ | \*text\* or \_text\_ | Ctrl/⌘ + I |
| ~~Strike-through~~ | \~text\~ or \~\~text\~\~ | ❔ |
| Link | [text]\(http://) | Ctrl/⌘ + K |
| Image | ![text]\(http://) | Ctrl/⌘ + Alt + I |
| `Inline Code` | \`code\` | ❔ |
| `Code Block` | \`\`\`code\`\`\` | Ctrl/⌘ + Alt + C or tab or 7 spaces |
| Unordered List | \* item <br></br> - item <br></br> + item | Ctrl/⌘ + L |
| Ordered List | 1. item | Ctrl/⌘ + Alt + L |
| Remove List | | Ctrl/⌘ + E |
| Blockquote | \> quote | Ctrl + ' or Ctrl + " |
| H1 | # Heading | Ctrl/⌘ + H |
| H2 | ## Heading | Ctrl/⌘ + H (×2) |
| H3 | ### Heading | Ctrl/⌘ + H (×3) |
### Lists
Enter a space in front of the asterisk or number to indent the list.
Copy this into your editor to see what it creates:
```
1. First ordered list item
2. Another item
* One space for unordered sub-list item
- One space for another sub-list item
* Press tab for sub-sub-list item
1. Two tabs for sub-sub-sub list item 😀
1. Actual numbers don't matter, just that it's a number
1. One space for ordered sub-list item
1. One space for another sub-list item
* Press Tab
1. One tab
* Two tabs. You got it! 👏
4. And another item. Success! 🎉
```
## Tables
Colons can be used to align columns.
Copy this into your editor to see what it creates:
```
| Tables | Are | Cool |
| ---------------- | :-----------: | --------: |
| col 2 is | centered | \$149 |
| col 3 is | right-aligned | \$4.17 |
| privacy is | neat | \$2.48 |
| rows don't need to |be pretty| what? |
| the last line is | unnecessary | really?
| one more | row | Yay! 😆
```
## Inline Styling
You can personalize the styling of the editor with inline HTML/CSS. For example, if you want to use monospace font for the editor, add this to your note, and replace `var(--sn-stylekit-monospace-font)` with your preferred font-families:
```html
<style>
.CodeMirror {
font-family: var(--sn-stylekit-monospace-font);
}
</style>
```
If you want to use monospace font for the preview, adjust the styles for `.editor-preview`:
```html
<style>
.CodeMirror,
.editor-preview {
font-family: var(--sn-stylekit-monospace-font);
}
</style>
```
#### Not yet available:
- Footnotes
- Superscript
- Subscript
- Syntax Highlighting
- Printing
- Default Custom Fonts
## Further Resources
- [GitHub](https://github.com/standardnotes/markdown-pro) - Development instructions, license (AGPL-3.0-or-later), and source code.

View File

@@ -0,0 +1,17 @@
---
id: 'plus-editor'
title: Plus Editor
sidebar_label: Plus Editor
description: How to use the Standard Notes Plus editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- plus editor
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Style Guide

View File

@@ -0,0 +1,38 @@
---
id: 'secure-spreadsheets'
title: Secure Spreadsheets Editor
sidebar_label: Secure Spreadsheets
description: How to use the Standard Notes secure spreadsheets editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- Template
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Introduction
Secure Spreadsheets is a [derived editor](https://standardnotes.com/help/77/what-are-editors). It is derived from [Kendo UI Professional](https://github.com/telerik/kendo-ui-core#features-of-kendo-ui-core).
A demo of the Secure Spreadsheets editor is available at [standardnotes.org/demo](https://standardnotes.com/demo). To use Secure Spreadsheets with Standard Notes, please sign up for [Standard Notes Extended](https://standardnotes.com/extensions) and install it by following the instructions described [here](https://standardnotes.com/help/29/how-do-i-install-extensions-once-i-ve-signed-up-for-extended).
## Export
You can export your spreadsheet as an `.xlsx` to use with Libre Office, Microsoft Office, Google Sheets, or other compatible program by clicking the download icon in the top left of the spreadsheets.
You can also export your spreadsheet as a `.pdf` file. The download menu has the following options:
- Export: Entire Workbook, Active Sheet, or Selection.
- Paper size: A2, A3, A4, A5, B3, B4, B5, Folio, Legal, Letter, Tabloid, and Executive.
- Margins: Normal and Wide.
- Orientation: Portrait or Landscape.
- With or without Guidelines (grid lines).
- Center: Horizontally, Vertically, Neither, or Both.
## License
The Secure Spreadsheets editor is derived from [Kendo UI Professional](https://github.com/telerik/kendo-ui-core#features-of-kendo-ui-core), which is not released as free and open-source software. To prevent vendor lock-in, you can easily [export your spreadsheet](#export) as an `.xlsx` or `.pdf` file.

View File

@@ -0,0 +1,29 @@
---
id: 'task-editor'
title: Simple Task Editor
sidebar_label: Task Editor
description: How to use the Standard Notes simple task editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- simple task editor
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Add Task
To add a task, enter the task into the box at the top then press enter/return.
## Rearrange Tasks
To rearrange tasks on the desktop or web apps, click on the task's checkbox then drag it up or down.
Rearranging tasks is currently unavailable on mobile.
## Delete Task
To delete a task, delete the task's text, then press enter/return. 😄

View File

@@ -0,0 +1,168 @@
---
title: Smart Views
sidebar_label: Smart Views
description: How to use the Standard Notes Smart Views.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- tags
- views
- filters
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
Questions:
- How do I view a list of untagged notes (and create other dynamic filters)?
## Introduction
“Smart Views" are user-made dynamic folders that organize your notes according to predefined filters.
For example, suppose you wanted to see a list of all notes whose title starts with “Foo”. You can do this by creating a smart tag.
## Creating A Smart View
1. Create a new folder by clicking the + icon.
1. Copy and paste the following Smart View syntax, as the folder name:
```
!["Foo Notes", "title", "startsWith", "Foo"]]
```
1. Press enter on your keyboard.
At this point, you should see an item called "Foo notes" under **Views**. You can select this item to view a list of your notes that start with “Foo”.
## Understanding The Smart View Syntax
Smart Views can be used to construct any kind of simple query. The components of the smart tag syntax are as follows:
`!`: Indicates the start of a Smart View
`[...]`: A JSON array
- The first item in the JSON array is the display label.
- The second item is the note attribute you are targeting.
- The third is the comparison operator.
- And the last is the expected value.
## More Examples
Show all notes that have tags that start with the letter b:
```
!["B-tags", "tags", "includes", ["title", "startsWith", "b"]]
```
Show all notes that have tags `Blog` or `Ideas`:
```
!["Blog or Ideas", "tags", "includes", ["title", "in", ["Blog", "Ideas"]]]
```
Show notes that are pinned:
```
!["Pinned", "pinned", "=", true]
```
Show notes that are not pinned:
```
!["Not Pinned", "pinned", "=", false]
```
Show notes that have been updated within the last day:
```
!["Last Day", "updated_at", ">", "1.days.ago"]
```
Show notes whose text has more than 500 characters:
```
!["Long", "text.length", ">", 500]
```
### Compound Predicates
You can use compound and/or predicates to combine multiple queries. For example, to show all notes that are pinned and locked:
```
!["Pinned & Locked", "ignored", "and", [["pinned", "=", true], ["locked", "=", true]]]
```
Show all notes that are protected or pinned:
```
!["Protected or Pinned", "ignored", "or", [["content.protected", "=", true], ["pinned", "=", true]]]
```
Show all notes that have tags `Blog` or `Ideas`.
```
!["Blog Scheduled or Published", "ignored", "or", [["tags", "includes", ["title", "=", "Blog"]], ["tags", "includes", ["title", "=", "Ideas"]]]]
```
You can also use the not predicate to negate an expression. For example, to show all notes that do not have the `Unread` tag:
```
!["Read", "tags", "not", ["tags", "includes", ["title", "=", "Unread"]]]
```
The not predicate can be combined with the compound operators. For example, to show all notes that have the Blog tag but not the Ideas one:
```
!["Blog Unpublished", "ignored", "and", [["tags", "includes", ["title", "=", "Blog"]], ["", "not", ["tags", "includes", ["title", "=", "Ideas"]]]]]
```
## Attributes
Here are a list of note attributes that can be queried:
- `title`
- `title.length`
- `text`
- `text.length`
- `tags`
- `updated_at`
- `created_at`
- `pinned`
- `content.protected`
If constructing a filter that queries tags, you can use the following tag attributes:
- `title`
- `title.length`
- `updated_at`
- `created_at`
Note that Smart Views always query notes, and so the query you're building refers to notes firstmost. You reference tags by referring to a note's tags:
```
!["B-tags", "tags", "includes", ["title", "startsWith", "b"]]
```
Get all notes whose tags includes a title that starts with the letter b.
## Operators
Here are a list of operators that can be used to construct filters. The operator is typically the third parameter in the filter syntax.
- `=`
- `>`
- `<`
- `>=`
- `<=`
- `startsWith`
- `in` ("whether a value is in a list of values")
- `includes` ("includes sub filter")
- `matches` (regex pattern)
- `and` (for compound filters)
- `or` (for compound filters)
- `not` (negates the expected value, attribute is ignored)

View File

@@ -0,0 +1,15 @@
---
id: 'token-vault'
title: Token Vault Editor
sidebar_label: Token Vault
description: How to use the Standard Notes Token Vault extension.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- Template
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---

View File

@@ -0,0 +1,15 @@
---
id: 'vim-editor'
title: Vim Editor
sidebar_label: Vim Editor
description: How to use the Standard Notes vim editor.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- vim editor
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---

View File

@@ -0,0 +1,38 @@
---
id: welcome
slug: /
title: 'Welcome to the Standard Notes Documentation'
sidebar_label: Welcome
description: Welcome to the Standard Notes Documentation
keywords:
- standard notes
- notes app
- end-to-end encryption
- welcome
image: /img/logo.png
hide_title: true
hide_table_of_contents: false
---
## Welcome to the Standard Notes Documentation
This documentation explains how to get started with using Standard Notes, publishing a blog with Listed, self-hosting a Standard Notes syncing server, developing an extension to work with Standard Notes, and developing a client application for Standard Notes (e.g., a command line application).
## Topics
- [Self-hosting a Syncing Server](/self-hosting/getting-started)
- [Developing Extensions](/extensions/intro)
- [Client Encryption API](/specification/encryption)
- [Sync API](/specification/sync)
- [Auth API](/specification/auth)
## Staying informed
- [GitHub](https://github.com/standardnotes)
- [Twitter](https://twitter.com/standardnotes)
- [Slack](https://standardnotes.com/slack)
- [Blog](https://blog.standardnotes.org)
## Something missing?
If you find issues with the documentation or have suggestions on how to improve the documentation or the project in general, please [file an issue](https://github.com/standardnotes/forum) on the forum or send a tweet mentioning [@standardnotes](https://twitter.com/standardnotes).