This discussion will be closed on August 2, 2021. Please, share your thoughts!
near-cli Core NEP
Summary
NEAR CLI
is one of the primary tools NEAR developers must use to develop and interact with NEAR networks and smart contracts. Today, NEAR CLI
contains the base commands for account creation and deletion, login, viewing account state and keys, sending tokens, creating staking transactions, deploying, and calling smart contracts.
This proposal will cover best practices that we want to follow during the development process, propositions to improve our key management and distribution systems, ideas on how to make NEAR CLI
more secure, modular, and configurable.
NEAR CLI
is an excellent tool to attract and engage new NEAR
developers, both core and contract level, and if it contains all the required features to select and configure and deploy NEAR
networks, nodes, crypto-assets (i.e., keys), accounts, and wallets, it can be the ideal single-entry point for those potential new hackers.
Motivation
These enhancements are intended to simplify and enhance developer and user productivity by providing a single and at the same time modular command-line tool that exercises all major features of NEAR
. As each major piece of NEAR
functionality is added to NEAR CLI
we should see the utilization of each of those features increase. Additionally, community mindshare and understanding of NEAR
should increase as NEAR CLI
will contain detailed help documentation for each major feature with clear examples of use, just like Linux man pages and/or other major CLI tools that have integrated help for each command.
First-class user experience
General principles
NEAR CLI
is interactive, prompts and advice are used over errors- Colors, highlighted text, animated loaders and progress bars are widely used
NEAR CLI
provides the possibility to setup auto-completion (generation of.env
file and instructions)- Common answers are saved into
config
file, frequent and identical prompts can be annoying NEAR CLI
uses default parameters, sometimes questions are redundantNEAR CLI
works out of the box, no initial setup is needed- Multi-language support (For
--help
, errors, and logs. Commands are not translated, language can be changed in config following the ISO 639-1) - All docs are available in
man near
,near --help
, andnear <command> --help
andnear help <command>
. There is no actual need to visit docs.near.org while working withNEAR CLI
- Only relevant flags and commands are shown in
near --help
andnear <command> --help
- All links are hyperlinks, nobody wants to copy them to the browser
- No magic! Users always know what they are doing
--verbose
flag can be used to show additional information about the execution process
Note: propose additional best practices that
NEAR CLI
should follow to improve user experience.
Two modes of interaction with the user
Since NEAR CLI
will not throw errors if some of the arguments were not specified, it will ask questions instead. It means that users will be able to work in two modes. The first one is for the engineers who are familiar with the CLI, and the second one is for easy, guided interaction.
- Example of classical mode where user is entering command as a one-liner.
near send testnet NEAR --sender serhii.testnet --receiver vlad.testnet --amount 10N
- Example of the interactive mode, where the user is guided step by step with prompts and questions (from https://github.com/FroVolod/near-cli):
serhii@x1 ~/P/N/h/n/n/t/debug (master)> ./near-cli
? Choose your action ›
✔ Choose your action · Transfer tokens
✔ What do you want to transfer? · NEAR tokens
? To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed.
✔ To construct a transaction you will need to provide information about sender (signer) and receiver accounts, and actions that needs to be performed.
Do you want to derive some information required for transaction construction automatically querying it online? · Yes, I keep it simple
✔ Select NEAR protocol RPC server: · Testnet
What is the account ID of the sender?: ⏎
These two modes can be mixed. If the user entered only half of the parameters then they will be asked about the remaining half.
Scripting friendly
- The output of the
NEAR CLI
is easy for parsing (--structured
flag will return result in JSON) - The output of the
NEAR CLI
can be piped to the input of anotherCLI
- Result of another tool can be used as an input to
NEAR CLI
(we should accept input asSTDIN
) NEAR CLI
should be OS agnostic (Linux, macOS and Windows)NEAR CLI
should use exit codes properly- Shell’s environment variables and any other environment variables should be available to the application
NEAR CLI
shall detect whether it’s been used in an interactive shell and if not (means scripting mode), it should not show interactive prompts but exit with error code and structured error message
Note: propose additional best practices that
NEAR CLI
should follow to improve scripting experience
Following industry best practices
POSIX standards
<>
for required parameters,[]
for optional parameters- All flags has short versions (
--force
has short version-f
) - Flags can be merged (
-a -b -c
is equal to-abc
) - We respect POSIX signals (such as Cntrl+C)
- etc.
Error handling
- Errors are
Tractable
(each error should have a code/name so it can be easily referenced) - Errors are
Actionable
(each error should give user a hint on how the error can be fixed) NEAR CLI
should have a debug mode with rich error output (provided with flag--verbose
)- When the error is thrown
NEAR CLI
should provide a link to theGitHub
issue template in theNEAR CLI
repository. The new issue should be prefilled with available parameters. - Never shows stack traces (avoid uncaught
throw
inJS
orpanic
inRust
) - When
--verbose
flag is not specifiedNEAR CLI
stores error log in a file, and mentions file location instderr
Note: The list of best practices can never be full. Anything we forgot about to mention? Add your propositions in the comments.
Secure
Key management
Current situation:
- Private keys are stored as a plain text in home directory
- Only one key is stored per account
In new NEAR CLI
it should look like:
- Private keys can be stored as plain text, in OS level key store or external key storage (like
bitwarden
) - Storage of any type can store multiple keys per account
- User can choose what key they wants to use
- Keys can not be overwritten, we should eliminate any possibility to lose keys
- Users can specify key management solution is
cli.config
- Each key is a separate file. It will prevent loss of all keys during the file read/write process.
Proposed file structure:
─ ~/.near-credentials
├── mainnet
├── alice
├── ed25519:<PK>.json
├── ed25519:<PK>.json
└── ed25519:<PK>.json
└── bob
├── ed25519:<PK>.json
├── ed25519:<PK>.json
└── ed25519:<PK>.json
├── testnet
├── alice
├── ed25519:<PK>.json
├── ed25519:<PK>.json
└── ed25519:<PK>.json
└── bob
├── ed25519:<PK>.json
├── ed25519:<PK>.json
└── ed25519:<PK>.json
└── ...
An example of ed25519:<PK>.json
file from .credentials/<network_name>/<account_id>
folder
{
"hd_path": "<HD Path>",
"private_key_location": "file", <- can be `ledger`, `system_key_store`, etc.
"private_key":"<key>"}⏎ <- can be empty if `private_key_location` is not `file`
}
Also, we are planning to investigate the approach of Amazon Vault system.
Note: if
Rust
will be chosen as the main language for the new NEAR CLI we will use.toml
files for all keys
Note: Well-designed key management is critical for our success. Please leave comments!
Accessible and well distributed
- Ship binaries for common OSes; distribute through
brew
,apt-get
,AUR
,yum
,chocolatey
,scoop
, etc. - Minimize use of production dependencies
- Cleanup configuration files during uninstall (but not keys!)
- Each release should be signed, users should be able to verify them
- Release process should be automated
Configurable
Certain NEAR CLI
functionality requires an input of the same data continuously, this information can be stored in the config
file not to bother our users.
NEAR CLI
will have 3 types of config files:
cli.config
for configurations that affects coreNEAR CLI
behaviorconnections.config
for the list of connections (connections are more flexible than networks)X-extension.config
for configurations that affectsNEAR CLI
extensions
Proposed file structure:
─ ~/.near-config
├── connections.config
├── cli.config
├── X-extension.config
└── Y-extension.config
-
connections.config
example:{ "mainnet": { nodeUrl: 'https://rpc.mainnet.near.org', walletUrl: 'https://wallet.near.org', helperUrl: 'https://helper.mainnet.near.org', helperAccount: 'near', explorerUrl: 'https://explorer.mainnet.near.org', }, "testnet": { ... }
-
cli.config
example{ "default_keystorage": "home_folder", ⟵ example: user chose to always save keys to the home directory when running "near login" "automatic_migrations": "always", ⟵ example: user wants to always run migrations automatically. "never" and "ask" options are also available "login_type": "browser" }
User will be able to manage this config with any text editor or built-in commands, such as
near config set <parameter> <value>
. -
X-extension.config
will similar tocli.config
Note: if
Rust
will be chosen as the main language for the newNEAR CLI
we will use.toml
files for all configs
Extendable
The current NEAR CLI
has many user types. Each of these user types requires its own set of functionality. At the same time, we are trying to keep NEAR CLI
minimalistic and easy to use. A variety of commands can be confusing.
The solution to this problem is extensions. It will allow the core NEAR CLI
to remain as lightweight as possible and satisfy all the demands from our growing user base at the same time.
Lack of extendability forces developers to create their own tools from scratch. Extendability within our tools alleviates this and reduces strain on our team, while also fostering a stronger dev community.
Here is a list of functions that can become an extension:
- Validation
- Project functionality (creation, building, testing, etc.)
- Linkdrop
- nearup
Note: Some of the proposed extensions can be a part of the core
NEAR CLI
, leave your thoughts in the comments.
Extension Platform VS Library
We will not try to build a whole extension platform. The extension should be a separate and independent library that can be executed with NEAR CLI
. NEAR CLI
extension developers should be able to reuse convenience components from NEAR CLI
(e.g. near-cli-key-management).
Note: Extensions are available in Rust. Good example is Cargo extensions.
Upgradable
As NEAR CLI
matures, updates may/will cause a user’s configuration to become outdated. This proposal declares that NEAR CLI
will have a mechanism to make such upgrades possible.
This endeavor cannot happen, however, without keeping track of which version of NEAR CLI
was most recently used. We will store this version in a cli.config
file.
Example:
...
"NEAR_CLI_VERSION": "2.4.0",
...
Migration scripts will be able to update all 4 types of config files (see Config section).
The folder structure within NEAR CLI
will have migration files per minor version as illustrated here:
└── middleware
└── migration
├── README.md
├── scripts
│ ├── 0.19.x.js ⟵ logic to run for 0.19.0 to 0.19.N
│ ├── 0.20.x.js ⟵ logic to run for 0.20.0 to 0.20.N
│ └── x.x.x-template.js ⟵ template for adding new migrations
└── shell-upgrade.js ⟵ checks for the current version against the stored version, etc.
Note: Migrations can also improve the experience by, for example, removing orphaned files, make safety checks and show warnings, etc.
Commands
The list of commands is highly affected by the design of our modular system and the decisions that we will make there. Here, we will try to explain the general approach.
near
- login
- send <network> <sender> <receiver> <amount> <- easy way to send tokens
- <other aliases of contract-transaction> <- we can move other popular functionality to the top level
- account <accountId>
- create-subaccount <subaccountId>
- state
- delete <beneficiaryId>
- keys <- will show account keys
- generate
- add
- delete
- construct-transaction <parameters>
- config
- set <parameter> <value>
- show <parameter> <- should show all available options aswell
----------------------------------------------------------------
The functionality of the `dev` extension is still under development, it can be structured differently:
- dev
- contract
- call
- view-call
- state
- deploy
- dev-deploy
- build
- test
- create-project
- repl
----------------------------------------------------------------
Validation extension:
- validators
- list <epoch>
- proposals
- stake (can be moved to top level as an alias)
---------------------------------------------------------------
Explorer extension:
- explorer
- tx-status
- etc. (all the other functions from the explorer)
---------------------------------------------------------------
Users will be able to write and add their own extensions.
Should we write new NEAR CLI
in Rust or JS/TS?
Motivation to write it in Rust
- We have a cool project from Vlad Frolov that we can base our new CLI on
- Reliable installation (a single binary installation that cannot get broken because of node.js upgrade or something like that)
- Predictable operation:
- scripting languages (JS, Python, …) are great for happy-path implementations, and really tough when you need to handle errors, so Rust just solidifies and secures the operations
- scripting languages require longer start-up (0.5 - 1 second of additional delay)
- Secure (single binary, reliable libraries)
Motivation to write it in JS/TS
- NEAR CLI in JS is already live in production
- Some say that it’s faster to develop software in JS/TS
- More people know JS/TS means more external contributions
- No build process during development! Just change code and execute
Some parts of the proposal are based on @mikedotexe work. Big thanks to @josh.quintal for collaboration and @frol for inspiration and Rust insights.
This discussion will be closed on August 2, 2021. Please, share your thoughts!