All skills

Sails.js framework patterns for The Boring JavaScript Stack - actions, helpers, routes, policies, hooks, configuration, security, middleware, file uploads, deployment, and more. Use this skill when building, reviewing, or debugging any server-side code in a Sails.js application.

Use this Skill: https://skilld.dev/gh/sailscastshq/boring-stack/sails

This session only. Nothing lands on disk.

rulesconfiguration.md

≈2.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Configuration

All Sails configuration lives in the config/ directory. Each file exports a key that merges into sails.config.

Config File Overview

File Access As Purpose
config/routes.js sails.config.routes URL-to-action mappings
config/policies.js sails.config.policies Action-to-policy mappings
config/models.js sails.config.models Default model settings
config/datastores.js sails.config.datastores Database connections
config/custom.js sails.config.custom App-specific settings
config/security.js sails.config.security CORS, CSRF settings
config/session.js sails.config.session Session store, cookies
config/globals.js sails.config.globals Global variable exposure
config/blueprints.js sails.config.blueprints Auto-generated REST routes
config/bootstrap.js sails.config.bootstrap Startup function
config/inertia.js sails.config.inertia Inertia.js settings (SSR, etc.)

Custom Configuration

config/custom.js is the place for all your app-specific settings:

// config/custom.js
module.exports.custom = {
  // App info
  appName: 'My App',
  appUrl: 'http://localhost:1337',

  // Email
  mailgunDomain: 'mg.example.com',
  mailgunApiKey: '',

  // Stripe
  stripeSecretKey: '',
  stripePublishableKey: '',
  stripePrices: {
    starter: 'price_xxx',
    pro: 'price_yyy'
  },

  // Feature flags
  enableBetaFeatures: false,
  verifyEmailAddresses: false,

  // TTL constants (time-to-live in milliseconds)
  passwordResetTokenTTL: 24 * 60 * 60 * 1000, // 24 hours
  emailProofTokenTTL: 24 * 60 * 60 * 1000, // 24 hours
  rememberMeCookieMaxAge: 30 * 24 * 60 * 60 * 1000, // 30 days

  // Email from address
  fromEmailAddress: 'noreply@example.com',
  fromName: 'My App Team',
  internalEmailAddress: 'support@example.com',

  // Domain validation lists
  bannedEmailDomainsForSubmissions: [
    'example.com',
    'gmail.com',
    'hotmail.com',
    'yahoo.com',
    'outlook.com',
    'icloud.com',
    'mail.com',
    'protonmail.com'
  ],

  // Regex for cleaning URLs
  RX_PROTOCOL_AND_COMMON_SUBDOMAINS: /^(https?\:\/\/)?(www\.|about\.)*/
}

Access anywhere: sails.config.custom.stripeSecretKey

Production-Sensitive Config Pattern

Use comments to document config values that should be set via environment variables in production, and only provide real values in config/env/production.js or config/local.js:

// config/custom.js
module.exports.custom = {
  // openAiSecret: undefined,
  // salesforceIntegrationUsername: undefined,
  // slackWebhookUrl: undefined,
}

Computed Config in Hooks

Config values can be computed at boot time in the custom hook's initialize:

// api/hooks/custom/index.js
sails.config.custom.enableBillingFeatures = !isMissingStripeConfig

Datastores

// config/datastores.js
module.exports.datastores = {
  default: {
    adapter: 'sails-postgresql',
    url: 'postgresql://user:pass@localhost:5432/myapp'
  }
}

Common adapters:

  • sails-postgresql -- PostgreSQL
  • sails-mysql -- MySQL
  • sails-mongo -- MongoDB
  • sails-disk -- Local disk (development only)

Models Configuration

// config/models.js
module.exports.models = {
  // Auto-migration strategy
  migrate: 'alter', // 'alter' (dev), 'safe' (production), 'drop' (reset)

  // Default attributes for ALL models
  attributes: {
    createdAt: { type: 'number', autoCreatedAt: true },
    updatedAt: { type: 'number', autoUpdatedAt: true },
    id: { type: 'string', columnName: '_id' }, // MongoDB style
    // or
    id: { type: 'number', autoIncrement: true } // SQL style
  },

  // Archive soft-deleted records
  archiveModelIdentity: 'archive',

  // Cascade deletes to associated records
  cascadeOnDestroy: true,

  // Return updated records after .update()
  fetchRecordsOnUpdate: true,
  fetchRecordsOnCreate: true,
  fetchRecordsOnCreateEach: true,
  fetchRecordsOnDestroy: false,

  // Data encryption key
  dataEncryptionKeys: {
    default: 'your-encryption-key-here'
  }
}

Session Configuration

// config/session.js
module.exports.session = {
  // Cookie name
  name: 'my-app.sid',

  // Session secret (auto-generated if not set)
  secret: process.env.SESSION_SECRET || 'default-dev-secret',

  // Cookie settings
  cookie: {
    maxAge: 24 * 60 * 60 * 1000, // 24 hours
    secure: false // Set true in production with HTTPS
  }

  // Redis session store (for production)
  // adapter: '@sailshq/connect-redis',
  // url: process.env.REDIS_URL,
}

Globals

// config/globals.js
module.exports.globals = {
  sails: true, // Access `sails` globally
  models: true, // Access models like `User` globally
  helpers: false, // Use `sails.helpers.*` instead
  _: false, // Disable lodash global
  async: false // Disable async global
}

Blueprints

The Boring Stack typically disables blueprint routes (since routes are explicit):

// config/blueprints.js
module.exports.blueprints = {
  actions: false, // No auto-routes for actions
  rest: false, // No auto-REST routes for models
  shortcuts: false // No shortcut routes
}

Bootstrap

config/bootstrap.js runs once when Sails starts:

// config/bootstrap.js
module.exports.bootstrap = async function () {
  // Seed database, warm caches, etc.
  const adminCount = await User.count({ role: 'admin' })
  if (adminCount === 0) {
    sails.log.info('No admin users found. Creating default admin...')
    await User.create({
      email: 'admin@example.com',
      password: await sails.helpers.passwords.hashPassword('changeme'),
      role: 'admin',
      fullName: 'Admin'
    })
  }
}

Environment-Specific Configuration

config/env/production.js

Overrides for production:

// config/env/production.js
module.exports = {
  datastores: {
    default: {
      adapter: 'sails-postgresql',
      url: process.env.DATABASE_URL,
      ssl: { rejectUnauthorized: false }
    }
  },
  models: {
    migrate: 'safe' // Never auto-migrate in production
  },
  session: {
    adapter: '@sailshq/connect-redis',
    url: process.env.REDIS_URL,
    cookie: {
      secure: true,
      maxAge: 24 * 60 * 60 * 1000
    }
  },
  security: {
    cors: {
      allRoutes: true,
      allowOrigins: [process.env.APP_URL]
    }
  },
  custom: {
    appUrl: process.env.APP_URL,
    stripeSecretKey: process.env.STRIPE_SECRET_KEY,
    mailgunApiKey: process.env.MAILGUN_API_KEY
  }
}

config/local.js

Local overrides (gitignored):

// config/local.js (not committed to git)
module.exports = {
  port: 1337,
  custom: {
    stripeSecretKey: 'sk_test_...',
    mailgunApiKey: 'key-...'
  }
}

Configuration Precedence (highest to lowest)

  1. Command-line args (sails lift --port=1338)
  2. Environment variables (sails_port=1492)
  3. .sailsrc in app directory
  4. Global ~/.sailsrc
  5. config/local.js
  6. config/env/* matching NODE_ENV
  7. Other files in config/

Environment Variables

Override any config using sails_ prefix with __ for nesting:

# Override port
sails_port=8080 sails lift

# Override datastore URL
sails_datastores__default__url='postgresql://...' sails lift

# Override custom config
sails_custom__stripeSecretKey='sk_test_...' sails lift

.sailsrc

Project-level Sails configuration:

{
  "hooks": {
    "grunt": false,
    "sockets": false,
    "pubsub": false
  },
  "generators": {
    "modules": {
      "page": "create-sails-generator"
    }
  }
}

The sails Object

The global sails object provides access to everything:

sails.config // All configuration
sails.config.custom // Custom app settings
sails.models // All models { user: User, team: Team }
sails.helpers // All helpers
sails.hooks // All hooks
sails.log // Logger (sails.log.info, .warn, .error, .debug)
sails.getDatastore() // Get default datastore

Source: SKILL.md on GitHub

1 alert17d4 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    This skill provides comprehensive documentation and coding patterns for the Sails.js framework as used in The Boring JavaScript Stack. It includes detailed guides on application anatomy, security best practices, and production deployment. No malicious patterns or security vulnerabilities were detected.

  • Socket17d

    2 alerts: gptSecurity

  • Snyk17d

    Risk: LOW · No issues

  • Runlayer7mo

    13/20 files flagged

Signed by skilld at bf19e10. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 days ago.

Activeupdated 8 months ago
Other metadata
metadata
{
  "author": "sailscastshq",
  "version": "2.1.0",
  "tags": "sails, sailsjs, backend, mvc, actions, helpers, routes, policies, hooks, middleware, deployment, boring-stack"
}

README badge

README badge for sailscastshq/boring-stack/sails