Thank you for your interest in building this thing together with us. We're really happy with our active community and are glad that you're a part of it. There are many ways to contribute to our project, but given the fact that you're on Github looking at the code for The Things Stack for LoRaWAN, you're probably here for one of the following reasons:
- Requesting a new feature: If you have a great idea or think some functionality is missing, we want to know! The only thing you have to do for that is to create an issue if it doesn't exist yet. Please use the issue template and fill out all sections.
- Reporting an issue: If you notice that a component of The Things Stack is not behaving as it should, there may be a bug in our systems. In this case you should create an issue if it doesn't exist yet. Please use the issue template and fill out all sections. For sensitive (security) issues, you can contact us directly.
- Implementing a new feature or fixing a bug: If you see an open issue that you would like to work on, let the other contributors know by commenting in the issue.
- Writing documentation: If you see that our documentation is lacking or incorrect, it would be great if you could help us improve it. This will help users and fellow contributors understand better how to work with our stack, and will prevent making mistakes and introducing bugs. Our documentation is spread across a number of places. Code documentation obviously lives together with the code, and is therefore probably in this repository. More general documentation lives in our
docs
repo, that is published to our official documentation pages.
Although The Things Network forums and community Slack are great ways to communicate with other community members, please use GitHub issues and pull requests to discuss matters related to the code base.
If you'd like to contribute by writing code, you'll find here how to set up your development environment. We also have some guidelines that describe how to make contributions that are consistent our way of working.
All branches shall have one of these names.
master
: the default branch. This is a clean branch where reviewed, approved and CI passed pull requests are merged into. Merging to this branch is restricted to project maintainersfix/#-short-name
orfix/short-name
: refers to a fix, preferably with issue number. The short name describes the bug or issuefeature/#-short-name
orfeature/short-name
: (main) feature branch, preferably with issue number. The short name describes the featurefeature/#-short-name-part
: a sub scope of the feature in a separate branch, that is intended to merge into the main feature branch before the main feature branch is merged intomaster
issue/#-short-name
: anything else that refers to an issue but is not clearly a fix nor a feature
A fix, feature or issue branch should be small and focused and should be scoped to a single specific task. Do not combine new features and refactoring of existing code.
Pull requests shall close or reference issues. Please file an issue first before submitting a pull request. When submitting a pull request, please fill out all the sections in the pull request template.
-
Before a reviewer is assigned, rebasing the branch to reduce the number of commits is highly advised. We recommend self-reviewing your own pull request: making the commit history clean, checking for typos or incoherences, and making sure Continuous Integration passes.
-
During a pull request's review, do not squash commits: it makes it harder for reviewers to read the evolution of a pull request. Making the commit history denser to answer reviewers' comments is acceptable at that point.
-
Once a pull request has been approved by the reviewers, it can be rebased on top of its target branch before it is merged. This is an opportunity for the contributor to clean up the commit history. A reviewer can also ask specifically for a rebase.
Keep the commits to be merged clean: adhere to the commit message format defined below and instead of adding and deleting files within a pull request, drop or fix the concerning commit that added the file.
Interactive rebase (git rebase -i
) can be used to rewrite commit messages that do not follow these contribution guidelines.
The first line of a commit message is the subject. The commit message may contain a body, separated from the subject by an empty line.
The subject contains the concerning component or topic and a concise message in the imperative mood, starting with a capital. The subject may also contain references to issues or other resources.
The component or topic is typically a few characters long and should always be present. Component names are:
api
: API, typically protosgs
: Gateway Serverns
: Network Serveras
: Application Serveris
: Identity Serverconsole
: Consolecli
: Command-line Interfaceutil
: utilitiesci
: CI instructions, e.g. Travis filedoc
: documentationdev
: other non-functional development changes, e.g. Makefile, .gitignore, editor configall
: changes affecting all code, e.g. primitive types
Changes that affect multiple components can be comma separated.
Good commit messages:
ns: Fix MIC check
dev: Set version from git tag, closes #123
ns,as,gs: Fix TLS check
Make sure that commits are scoped to something meaningful and could, potentially, be merged individually.
The body may contain a more detailed description of the commit, explaining what it changes and why. The "how" is less relevant, as this should be obvious from the diff.
We want our code to be consistent across our projects, so we'll have to agree on a number of formatting rules. These rules should usually usually be applied by your editor. Make sure to install the editorconfig plugin for your editor.
Go code can be automatically formatted using the gofmt
tool. There are many editor plugins that call gofmt
when you save your files.
We use utf-8, LF line endings, a final newline and we trim whitespace from the end of the line (except in Markdown).
Many developers have strong opinions about using tabs vs spaces. We apply the following rules:
- All
.go
files are indented using tabs - The
Makefile
and all.make
files are indented using tabs - All other files are indented using two spaces
- If a line is longer than 80 columns, try to find a "natural" break
- If a line is longer than 120 columns, insert a line break
- In very special cases, longer lines are tolerated
We use golint
to lint .go
files and eslint
to lint .js
and .vue
files. These tools should automatically be installed when initializing your development environment.
All API method names should follow the naming convention of VerbNoun
in upper camel case, where the verb uses the imperative mood and the noun is the resource type.
The following snippet defines the basic CRUD definitions for a resource named Type
.
Note also that the order of the methods is defined by CRUD.
CreateType
GetType
ListTypes (returns slice)
UpdateType
DeleteType
AddTypeAttribute
SetTypeAttribute
GetTypeAttribute
ListTypeAttributes (returns slice)
RemoveTypeAttribute
Variable names should be short and concise.
We follow the official go guidelines and try to be consistent with Go standard library as much as possible, everything not defined in the tables below should follow Go standard library naming scheme. In general, variable names are English and descriptive, omitting abbreviations as much as possible (except for the tables below), as well as putting adjectives and adverbs before the noun and verb respectively.
entity | name | example type |
---|---|---|
context | ctx | context.Context |
mutex | mu | sync.Mutex |
configuration | conf | go.thethings.network/lorawan-stack/pkg/config.Config |
logger | logger | go.thethings.network/lorawan-stack/pkg/log.Logger |
message | msg | go.thethings.network/lorawan-stack/api/gateway.UplinkMessage |
status | st | go.thethings.network/lorawan-stack/api/gateway.Status |
server | srv | go.thethings.network/lorawan-stack/pkg/network-server.Server |
ID | id | string |
unique ID | uid | string |
counter | cnt | int |
gateway | gtw | |
application | app | |
end device | dev | |
user | user | |
transmit | tx / Tx | |
receive | rx / Rx |
The EUI naming scheme can be found in the well-known variable names section bellow.
In case both of the words have an implementation-specific meaning, the variable name is the combination of first letter of each word.
entity | name |
---|---|
wait group | wg |
Gateway Server | gs |
Network Server | ns |
Join Server | js |
Application Server | as |
Identity Server | is |
In case one of the words specifies the meaning of the variable in a specific language construct context, the variable name is the combination of abbrevations of the words.
These are the names of variables that occur often in the code. Be consistent in naming them, even when their meaning is obvious from the context.
entity | name |
---|---|
gateway ID | gtwID |
gateway EUI | gtwEUI |
application ID | appID |
application EUI | appEUI |
join EUI | joinEUI |
device ID | devID |
device EUI | devEUI |
user ID | userID |
Events are defined with events.Define("event_name", "event description")
The event name is usually of the form component.entity.action
. Examples are ns.up.receive_duplicate
and is.user.update
. We have some exceptions, such as ns.up.join.forward
, which is specifically used for join messages. The Device Registry (which is used by both the NS, AS and JS) currently publishes events such as device.create
. In the future we may also add component prefixes there. See below for naming conventions.
The event description describes the event in simple English. The description is capitalized by the frontend, so the message should be lowercase, and typically doesn't end with a period.
Errors are defined with errors.Define<Type>("error_name", "error description with `{attribute_value}`")
Error definitions must be defined as close to the return statements as possible; in the same package, and preferably above the concerning function(s). Do not export the error definitions unless they are meaningful to other packages, i.e. for testing the exact error definition.
Prefer using a specific error type, i.e. errors.DefineInvalidArgument()
. If you are using a cause (using WithCause()
), you may use Define()
to fallback to the cause's type.
The error name in snake case is a short and unique identifier of the error within the package. There is no need to append _failed
or _error
or prepend failed_to_
as an error already indicates something went wrong. Be consistent in wording (i.e. prefer the more descriptive missing_field
over no_field
), order (i.e. prefer the more clear missing_field
over field_missing
) and do not use entity abbreviations.
The error description in lower case, with only names in title case, is a concise plain English text that is human readable and understandable. Do not end the description with a dot. You may use attributes, in snake case, in the description defined between backticks (`
) by putting the key in curly braces ({ }
). See below for naming conventions. Only provide primitive types as attribute values using WithAttributes()
.
Any name
defined in the following statements:
- Logging field key:
logger.WithField("name", "value")
- Event name:
events.Define("name", "description")
- Error name:
errors.Define("name", "description")
- Error attribute:
errors.Define("example", "description `{name}`")
- Task identifier:
c.RegisterTask("name", ...)
Shall be snake case, optionally having an event name prepended with a dotted namespace, see above. The spacer _
shall be used in LoRaWAN terms: DevAddr
is dev_addr
, AppSKey
is app_s_key
, etc.
Code should be as self-explanatory as possible. However, comments should be used to respect Go formatting guidelines, to generate insightful documentation with Godoc, and to explain what can not be expressed by pure code. Comments should be English sentences, and documentation-generating comments should be closed by a period. Comments can also be used to indicate steps to take in the future (TODOs), if they reference the GitHub issue to track this TODO.
-
In Go files, comments should be added according to
golint
requirements and Effective Go guidelines, especially in regards to commenting exported packages, types and variables. -
In protocol buffer files, fields should be concisely commented.
-
Protobuf-generated files, including Go files, are not tracked by
golint
. These do not need to respect the same comments guidelines as Go files, especially in regards to exported values.
We do our best to make all text that could be visible to users available for translation. This means that all text of the console's user interface, as well as all text that it may forward from the backend, needs to be defined in such a way that it can be translated into other languages than English.
In the API, the enum descriptions, error messages and event descriptions available for translation. Enum descriptions are defined in pkg/ttnpb/i18n.go
. Error messages and event descriptions are defined with errors.Define(...)
and events.Define(...)
respectively.
These messages are then collected in the config/messages.json
file, which will be processed in the frontend build process, but may also be used by other (native) user interfaces. When you define new enums, errors or events or when you change them, the messages need to be updated into the config/messages.json
file.
./mage go:messages
If you forget to do so, this will cause a CI failure.
Adding translations of messages to other languages than English is a matter of adding key/value pairs to translations
in config/messages.json
.
Translations of frontend messages are located in pkg/webui/locales
.
We do our utmost best to build secure systems, but we're human too, so we sometimes make mistakes. If you find any vulnerability in our systems, please contact us directly. We can be reached on Slack, by email and a number of other communication platforms.
- Johan Stokking - keybase.io/johanstokking
EE80D01EB2BE7EC8
- Hylke Visser - keybase.io/htdvisser
A115FF80DC8A2270
Our email addresses follow the pattern <firstname>@thethingsnetwork.org
.
The Things Stack for LoRaWAN is Apache 2.0 licensed.