Skip to content

Missing Administrator Account

Rodrigo Silveira edited this page Jul 24, 2026 · 5 revisions

Missing Administrator Account

Stop the Bite 28C backend

Only one backend should access the local SQLite database during this procedure.

Stop the running backend with Ctrl+C.

2. Create a temporary Development worktree

cd /Users/rodrigosilveira/projects/EnterpriseRemoteSystems

git fetch origin

git worktree add --detach \
  ../EnterpriseRemoteSystems-development-bootstrap \
  origin/development

Confirm that Development contains Bite 28B authentication:

test -f \
  ../EnterpriseRemoteSystems-development-bootstrap/backend/migrations/000042_user_authentication_foundation.up.sql \
  && echo "Bite 28B authentication is present"

If that file is missing, do not continue with this procedure.

3. Point Development at the existing Bite 28C local database

Check the database configuration in your main working tree:

cd /Users/rodrigosilveira/projects/EnterpriseRemoteSystems

grep -E '^(DB_PATH|DATABASE_PATH)=' backend/.env

If neither variable is present, the normal default database is:

/Users/rodrigosilveira/projects/EnterpriseRemoteSystems/backend/data/app.db

Set the exact database file:

LOCAL_DB="/Users/rodrigosilveira/projects/EnterpriseRemoteSystems/backend/data/app.db"

Adjust that value if your .env specifies another file.

Confirm it exists:

ls -lh "$LOCAL_DB"

4. Start the Development backend against that database

cd /Users/rodrigosilveira/projects/EnterpriseRemoteSystems-development-bootstrap

DB_PATH="$LOCAL_DB" make local-backend

Leave this terminal running.

Bite 28C did not introduce a new database migration in the files you listed, so the Bite 28B authentication schema should remain compatible.

5. Inspect the bootstrap administrator actor

Set the API address:

BASE_URL="http://127.0.0.1:8080/api/v1"

Retrieve the Actor records and preserve the complete response:

HTTP_STATUS=$(
  curl -sS \
    -D /tmp/ers-development-authz-actors.headers \
    -o /tmp/ers-development-authz-actors.json \
    -w '%{http_code}' \
    -H "X-Actor-ID: bootstrap-admin" \
    -H "X-Tenant-ID: default" \
    "$BASE_URL/authz/actors"
)

echo "HTTP $HTTP_STATUS"
jq . /tmp/ers-development-authz-actors.json

Expected:

HTTP 200

Now extract bootstrap-admin using a query that supports both possible response shapes:

jq -e '
  def actors:
    if (.data | type) == "array" then
      .data
    elif (.data.items | type) == "array" then
      .data.items
    else
      []
    end;

  actors
  | map(select(.actorKey == "bootstrap-admin"))
  | first
  | if . == null then
      error("bootstrap-admin was not found")
    else
      {
        actorRecordId: .id,
        actorKey: .actorKey,
        displayName: .displayName,
        active: .active,
        applicationAdminGrants: [
          (.roleGrants // .grants // [])[]
          | select(
              .roleCode == "APPLICATION_ADMIN"
              and .tenantId == "*"
            )
        ]
      }
    end
' /tmp/ers-development-authz-actors.json

Expected result should resemble:

{
  "actorRecordId": "some-internal-uuid",
  "actorKey": "bootstrap-admin",
  "displayName": "Bootstrap Administrator",
  "active": true,
  "applicationAdminGrants": [
    {
      "roleCode": "APPLICATION_ADMIN",
      "tenantId": "*",
      "active": true
    }
  ]
}

Confirm:

  • actorKey is bootstrap-admin.
  • active is true.
  • applicationAdminGrants contains at least one record.
  • Its roleCode is APPLICATION_ADMIN.
  • Its tenantId is *.
  • If the response includes grant active, it is true.

Extract the internal Actor record ID

After the preceding command succeeds:

BOOTSTRAP_ACTOR_RECORD_ID=$(
  jq -er '
    def actors:
      if (.data | type) == "array" then
        .data
      elif (.data.items | type) == "array" then
        .data.items
      else
        []
      end;

    actors[]
    | select(.actorKey == "bootstrap-admin")
    | .id
  ' /tmp/ers-development-authz-actors.json
)

echo "Bootstrap Actor record ID: $BOOTSTRAP_ACTOR_RECORD_ID"

The result should be a UUID or other internal database identifier—not:

bootstrap-admin

bootstrap-admin is the Actor key; BOOTSTRAP_ACTOR_RECORD_ID is the internal Actor record ID required when creating the authentication account.

If the command says that bootstrap-admin was not found, list the available Actor keys:

jq -r '
  if (.data | type) == "array" then
    .data
  elif (.data.items | type) == "array" then
    .data.items
  else
    []
  end
  | .[]
  | [.actorKey, .displayName, (.active | tostring)]
  | @tsv
' /tmp/ers-development-authz-actors.json

6. Check whether it already has an authentication account

curl -sS \
  -H "X-Actor-ID: bootstrap-admin" \
  -H "X-Tenant-ID: default" \
  "$BASE_URL/auth/accounts" \
| jq \
  --arg actorId "$BOOTSTRAP_ACTOR_RECORD_ID" \
  '.data[]
   | select(.actorId == $actorId)
   | {
       accountId: .id,
       login,
       active,
       actorActive,
       mustChangePassword
     }'

Proceed according to the result.

Option A — No account exists

Choose credentials:

ADMIN_LOGIN="local.application.admin@example.com"
ADMIN_PASSWORD='Local-ERS-Admin-Password-28C!'

Create the account:

jq -n \
  --arg actorId "$BOOTSTRAP_ACTOR_RECORD_ID" \
  --arg login "$ADMIN_LOGIN" \
  --arg password "$ADMIN_PASSWORD" \
  '{
    actorId: $actorId,
    login: $login,
    temporaryPassword: $password,
    mustChangePassword: false
  }' \
| curl -sS \
    -D /tmp/ers-create-local-admin.headers \
    -o /tmp/ers-create-local-admin.json \
    -w 'HTTP %{http_code}\n' \
    -H "X-Actor-ID: bootstrap-admin" \
    -H "X-Tenant-ID: default" \
    -H "Content-Type: application/json" \
    --data-binary @- \
    "$BASE_URL/auth/accounts"

cat /tmp/ers-create-local-admin.headers
jq . /tmp/ers-create-local-admin.json

Expected:

HTTP 201

Option B — An account exists, but its password is unknown

Extract its account ID and login:

ACCOUNT_JSON=$(
  curl -sS \
    -H "X-Actor-ID: bootstrap-admin" \
    -H "X-Tenant-ID: default" \
    "$BASE_URL/auth/accounts" \
  | jq -c \
      --arg actorId "$BOOTSTRAP_ACTOR_RECORD_ID" \
      '.data[]
       | select(.actorId == $actorId)'
)

ACCOUNT_ID=$(jq -er '.id' <<<"$ACCOUNT_JSON")
ADMIN_LOGIN=$(jq -er '.login' <<<"$ACCOUNT_JSON")
ADMIN_PASSWORD='Local-ERS-Admin-Password-28C!'

echo "Account: $ACCOUNT_ID"
echo "Login:   $ADMIN_LOGIN"

Issue a reset token:

RESET_TOKEN=$(
  curl -sS \
    -X POST \
    -H "X-Actor-ID: bootstrap-admin" \
    -H "X-Tenant-ID: default" \
    "$BASE_URL/auth/accounts/$ACCOUNT_ID/password-reset-tokens" \
  | jq -er '.data.token'
)

echo "Reset token obtained."

Reset the password:

jq -n \
  --arg token "$RESET_TOKEN" \
  --arg password "$ADMIN_PASSWORD" \
  '{
    token: $token,
    newPassword: $password
  }' \
| curl -sS \
    -o /tmp/ers-reset-local-admin.json \
    -w 'HTTP %{http_code}\n' \
    -H "Content-Type: application/json" \
    --data-binary @- \
    "$BASE_URL/auth/password/reset"

Expected:

HTTP 204

The reset token is one-time use and should not be retained.

7. Stop Development and restart Bite 28C

Stop the Development backend with Ctrl+C.

Then restart the backend from the real Bite 28C working tree:

cd /Users/rodrigosilveira/projects/EnterpriseRemoteSystems

make local-backend

8. Verify the new Bite 28C login

In another terminal:

BASE_URL="http://127.0.0.1:8080/api/v1"
ADMIN_COOKIE="/tmp/ers-b28c-admin.cookies"

rm -f "$ADMIN_COOKIE"

Use the ADMIN_LOGIN and ADMIN_PASSWORD established above:

jq -n \
  --arg login "$ADMIN_LOGIN" \
  --arg password "$ADMIN_PASSWORD" \
  '{
    login: $login,
    password: $password
  }' \
| curl -sS \
    -D /tmp/ers-b28c-admin-login.headers \
    -o /tmp/ers-b28c-admin-login.json \
    -w 'HTTP %{http_code}\n' \
    -c "$ADMIN_COOKIE" \
    -H "Content-Type: application/json" \
    --data-binary @- \
    "$BASE_URL/auth/login"

jq . /tmp/ers-b28c-admin-login.json

Expected:

HTTP 200

Then verify Application Admin access:

curl -sS \
  -D /tmp/ers-b28c-admin-actors.headers \
  -o /tmp/ers-b28c-admin-actors.json \
  -w 'HTTP %{http_code}\n' \
  -b "$ADMIN_COOKIE" \
  -H "X-Tenant-ID: default" \
  "$BASE_URL/authz/actors"

jq . /tmp/ers-b28c-admin-actors.json

Expected:

HTTP 200

9. Remove the temporary worktree

After the administrator login works under Bite 28C:

cd /Users/rodrigosilveira/projects/EnterpriseRemoteSystems

git worktree remove \
  ../EnterpriseRemoteSystems-development-bootstrap

Clone this wiki locally