npm package: @heroku/skynet
Description: use Skynet from Heroku CLI
Latest version: 2.5.0

# Skynet CLI Plugin 
[![Build Status](https://travis-ci.com/heroku/heroku-skynet-cli.svg?token=Tja9HVA663wD1FA6ZNZn&branch=master)](https://travis-ci.com/heroku/heroku-skynet-cli)

Use Skynet from Heroku CLI

Most Skynet CLI commands require sudo and only the folowing teams are authrozied to use Skynet CLI:
* heroku-secops
* heroku-security
* heroku-support
* heroku-cedar
* heroku-dogwood

### Break-glass direct suspend (`skynet:suspend:user:direct`)

When Skynet is unavailable, you can suspend users or apps directly via the Heroku API using the hidden command `heroku sudo skynet:suspend:user:direct`. The command will prompt for a **Heroku Platform API key** (it is not stored).

**Getting the platform user API token**

Use an account that is authorized for suspension (e.g. a platform/ops user or your own account if you are on an authorized team). Then obtain an API key in one of these ways:

1. **From the Heroku Dashboard**  
   Log in as the platform user → [Account settings](https://dashboard.heroku.com/account) → **API Key** section → click "Reveal" or "Regenerate API Key" and copy the key.

2. **From the CLI**  
   Log in as the platform user (`heroku login`) and run:
   ```bash
   heroku auth:token
   ```
   Use the printed token when the command prompts for "Heroku Platform API Key".

The token is only used for the duration of the command and is not saved. After running a direct suspend, upload the local suspension record to Skynet once it is back up: `heroku skynet:suspensions:upload`.

### Legal Hold Suspensions

User suspensions can be marked with legal hold status using the `--legal-hold` flag. Legal hold suspensions:
- Cannot be unsuspended until the hold is cleared by authorized personnel
- Are tracked for legal/compliance purposes
- May have data retention requirements

```bash
heroku sudo skynet:suspend:user -u user@example.com -c "legal" -n "legal hold required" --legal-hold
```

Legal hold is also supported for break-glass direct suspensions.

### Managing Legal Hold Flags

You can add or remove legal hold flags independently of suspension status:

```bash
# Add legal hold to a user
heroku skynet:legal-hold:add -u user@example.com

# Remove legal hold from a user
heroku skynet:legal-hold:remove -u user@example.com
```

These commands manage legal hold as a standalone flag/marker on user accounts.

### Installation
```
heroku plugins:install heroku-skynet-cli
```

### Development
- `git clone https://github.com/heroku/heroku-skynet-cli.git`
- Change directories into the cloned repository 
- Link the plugin with `heroku plugins:link .`

### Manual testing

1. Make sure your staging account is setup: https://github.com/heroku/engineering-docs/blob/master/guides/staging-account-setup.md
1. Get someone to add you to the `heroku-security` team/org in [staging](https://dashboard-api-staging.herokuapp.com/teams/heroku-security/access)
1. Make sure you have the latest ion-client installed:

    ```
    gem install ion-client
    ```

    If you get permissions errors against packagecloud, visit https://gemgate-heroku-internal-gems.herokuapp.com/setup-instructions


In skynet-cli directory, use the following:

```
heroku plugins:link .
export SKYNET_HOST=https://skynet-staging.risk.herokai.com
cloud staging
heroku login # login to the staging cloud via the cli
heroku skynet:<cmd>
```

### Unit test
```bash
# $SKYNET_CLI_HOME points to the home dir of heroku-skynet-cli 
cd $SKYNET_CLI_HOME
unset SKYNET_HOST
npm test
```

### Integration test
```bash
#
# Start local skynet
#

# start postgres DB and redis
$ pg_ctl -D /usr/local/var/postgres start
$ redis-server

# Update URL and KEY in .env. $REAL_KEY_IN_PRODUCTION refers to the real key in production.
$ cd $SKYNET_HOME
$ sed -i -e 's/HEROKU_API_URL=.*/HEROKU_API_URL=https:\/\/api.heroku.com/g' .env  
$ sed -i -e 's/HEROKU_API_KEY=.*/HEROKU_API_KEY=$REAL_KEY_IN_PRODUCTION/g'  .env

# Start skynet instance
$ cd $SKYNET_HOME
$ heroku local web,worker

# 
# Config heroku-skynet-cli
#

# Pointing CLI to the Heroku Production
$ cd $HEROKU_SKYNET_CLI_HOME
$ cloud production

# Link to the local plugin implementation
$ cd $HEROKU_SKYNET_CLI_HOME
$ heroku plugins:link

# Pointing to the local Skynet instance
$ export SKYNET_HOST=http://localhost:5000

#
# Run Tests
#

# This unsuspend request will be sent to the local Skynet instance
$ heroku sudo skynet::unsuspend::user -u xyz@heroku.com 

# Verify that you can see the unsuspend record in suspension_activities table 
```

### Publishing to `npm`
* Request access to the NPM Heroku Organization using [this form](https://securityorg.force.com/IntakeApp/s/?entityCode=HerokuAccess&si=a1i3A0000007euiQAA).
* Request write access to @heroku/skynet package by sending email to dzhuo@salesforce.com.
* Publish the package:

```
# Check out the latest master
$ cd $HEROKU_SKYNET_CLI_HOME
$ git checkout -b master origin/master

# Run npm release. In case `npm run release` asks you whether 
# or not you want to publish the repo publicly, answer "No".
$ npm login
$ npm test
$ npm run release
```

* Command 'npm run release' will change the version number in package.json file and commit. but the pull request will fail due to the enabled git pull process. A follow-up manual pull request is needed to check in the new version number in Git repository.    
* Verify that new package version is created on [npm website](https://www.npmjs.com/package/@heroku/skynet), or directly run command "npm view @heroku/skynet"
* Note: You will get an error at the end of `npm run release`. Np attempts to commit to master branch the updated package version in the package.json. But the commit will fail because our master branch is protected. You can ignore the error message. 
* Submit a new PR to update the version in `package.json` and `package-lock.json`. Here is one [PR example](https://github.com/heroku/heroku-skynet-cli/pull/74).
* Test the newly released @heroku/skynet package

```bash
# install the latest @heroku/skynet package
$ cd $HEROKU_SKYNET_CLI_HOME
$ heroku plugins:unlink
$ heroku plugins:install @heroku/skynet
$ heroku plugins  # verify that the latest version is installed

# run tests
$ heroku sudo skynet::unsuspend::user -u xyz@heroku.com 
```