Oolvay centralizes application behavior in the config/ directory.
Rather than scattering settings across routes, components, server actions, and utility functions, most configurable behavior is exposed through a small collection of dedicated configuration files. These files act as the application’s source of truth for branding, authentication, billing, metadata, email delivery, security controls, observability, and platform behavior.
config/
├── api-keys.ts
├── constants.ts
├── emails.ts
├── metadata.ts
├── pricing.ts
└── site.tsMany features have their own dedicated documentation that discusses configuration in greater detail. This page serves as a high-level map of the configuration system and explains where each type of setting lives.
Oolvay intentionally separates application behavior from deployment secrets.
Application behavior is configured through files in the config/ directory and is typically committed to source control. Deployment-specific values such as API credentials and connection strings are supplied through environment variables.
| Location | Purpose |
|---|---|
config/* | Application behavior and feature configuration |
.env.local | Secrets and deployment-specific values |
For example:
0 / 2,000 characters
| Configuration file |
|---|
| Purpose |
|---|
config/pricing.ts | Defines plans, limits, and feature entitlements |
config/emails.ts | Defines sender identities and email behavior |
config/site.ts | Defines platform-wide behavior |
.env.local | Stores provider credentials and secrets |
A useful rule of thumb is that if a value would be the same in every deployment of your application, it probably belongs in config/. If it changes between environments or contains sensitive information, it probably belongs in .env.local.
Brand-related values are defined in constants.ts.
export const BRANDNAME = "Acme"
export const BRANDDOMAIN = "acme.com"
export const EMAILSENDERNAME = "John Doe"This file is usually one of the first files you modify once you clone Oolvay. If you install via CLI, BRANDNAME and BRANDDOMAIN are automatically updated for you.
These values are reused throughout the application to avoid duplicating branding information in multiple places. Changing them affects:
For most projects, rebranding can be accomplished by updating this file and reviewing any remaining hardcoded references.
Page metadata is defined in metadata.ts.
export const metaData = {
home: {
title: "...",
description: "...",
},
}This file controls metadata for both public and authenticated pages.
Common settings include:
Most authenticated pages disable indexing.
robots: {
index: false,
follow: false,
}This prevents search engines from indexing private areas such as dashboards, account settings, notifications, and administrative interfaces. Public-facing pages such as pricing, documentation, blog posts, and marketing content remain indexable by default. For detailed implementation guideance on metadata, see the Metadata documentation.
Email behavior is defined in emails.ts.
export const emails = {
provider: "resend" as "resend" | "postmark" | "ses",
deploySesInfrastructure: false,
support: {},
contact: {},
welcome: {},
subscriptions: {},
magicLink: {},
privacy: {},
grievance: {},
}This file serves two purposes:
Provider credentials are supplied through environment variables and are documented separately. This file only controls how the application uses those providers.
provider: "resend" as "resend" | "postmark" | "ses"Determines which transactional email provider the application uses.
Supported providers are:
Switching providers typically requires updating environment variables but does not require application code changes.
If you are using Resend and have not yet configured a custom sending domain, you will notice that many examples in this documentation use onboarding@resend.dev.
This is Resend’s default testing sender address. Until a custom domain is verified, Resend only allows email to be sent from one of its managed testing addresses and does not permit arbitrary sender addresses such as hello@acme.com or support@acme.com.
There is a second limitation worth keeping in mind. In testing mode, Resend only delivers email to the email address associated with your Resend account. As a result, any configuration values that receive email, such as contact.toEmail, should temporarily use that address if you want to test delivery successfully.
Once you verify a custom domain with Resend, you can replace onboarding@resend.dev throughout config/emails.ts with addresses that match your brand, such as:
hello@acme.com
support@acme.com
accounts@acme.com
privacy@acme.com
grievance@acme.comAt that point, Resend can send to arbitrary recipients and the testing restrictions no longer apply.
Postmark and Amazon SES have different sender-verification requirements and do not provide an equivalent to Resend’s onboarding@resend.dev testing address. Consult the provider-specific setup documentation before configuring production sender addresses. Also see Emails documentation for a deeper dive on the subject.
deploySesInfrastructure: falseWhen using Amazon SES, Oolvay can optionally provision SES-related infrastructure through AWS CDK.
Leave this disabled unless:
Projects using Resend or Postmark will typically never need to modify this setting.
support: {
sender: `Team ${BRANDNAME}`,
email: "onboarding@resend.dev",
}Defines the support identity displayed throughout the application.
This address is commonly surfaced in:
contact: {
sender: `Team ${BRANDNAME}`,
fromEmail: "contact@acme.com",
toEmail: "onboarding@resend.dev",
}Controls how messages submitted through the contact form are routed.
fromEmail determines the sender identity used when the application generates the email.
toEmail determines the mailbox that receives contact form submissions.
welcome: {
sender: `${EMAILSENDERNAME} from ${BRANDNAME}`,
fromEmail: "onboarding@resend.dev",
}Controls the sender identity used for onboarding and welcome emails.
Using a named individual often feels more personal than using a generic company sender and can improve engagement with onboarding emails.
subscriptions: {
sender: `${BRANDNAME} Blog`,
fromEmail: "onboarding@resend.dev",
}Controls the sender identity used for blog notification emails. This sender is used when notifying subscribers about newly published blog posts.
The configured sender name and email address determine how these notifications appear in a recipient’s inbox and can be customized independently of other email categories such as support, onboarding, authentication, privacy, and grievance communications.
magicLink: {
sender: `${BRANDNAME} Accounts`,
fromEmail: "onboarding@resend.dev",
expiresInSeconds: 300, // 5 minutes
}Controls passwordless authentication emails.
expiresInSeconds determines how long a sign-in link remains valid after it is generated. Short expiration windows reduce the impact of leaked or intercepted links while still providing a reasonable user experience.
privacy: {
sender: `${BRANDNAME} Privacy Officer`,
toEmail: `privacy@${BRANDDOMAIN}`,
}Defines the contact information used for privacy-related requests.
Examples include:
grievance: {
sender: `${BRANDNAME} Grievance Officer`,
toEmail: `grievance@${BRANDDOMAIN}`,
}Defines the contact information used for formal complaints and grievance submissions.
This is particularly important for businesses operating in jurisdictions that require designated grievance contacts or regulatory complaint channels.
Subscription plans and entitlements are defined in pricing.ts.
export const TIERS = {
starter: {},
pro: {},
business: {},
}This file controls:
Plan ordering is significant.
// Tiers must be defined from lowest to highest.
// requireTier() uses this order for access control comparisons.Changing the order of plans may affect authorization logic throughout the application.
Provider-specific plan mappings are also defined here.
export const PRICE_MAP = {
pro_monthly: {
lemonsqueezy: "...",
razorpay: "...",
},
}This allows a single internal plan definition to map cleanly to multiple payment providers. For complete billing configuration guidance, see the Payments documentation.
API key behavior is defined in api-keys.ts.
export const API_KEY_SCOPES = ["read", "write", "delete", "admin"] as constThis file controls:
The starter kit ships with example scopes intended as placeholders rather than a fixed access model.
export const API_KEY_MAX_PER_USER = 20This limit is enforced server-side and cannot be bypassed through client requests.
For a complete walkthrough of key creation, revocation, auditing, and authorization considerations, see the API Keys documentation.
Most platform-wide behavior is defined in site.ts.
export const siteConfig = {
...
}The remainder of this page covers the major configuration domains exposed through this file.
Authentication settings control session lifecycle behavior.
authAndSession: {
callbackAfterLogin: "/dashboard",
expiresInDays: 30,
updateAgeInDays: 1,
cookieMaxAgeInMinutes: 5,
}These settings influence:
For most applications, the session expiration settings are the values most likely to require adjustment.
Authentication providers and sign-in methods are documented separately in the Authentication section.
Profile validation limits are configured centrally.
users: {
maxNameLength: 100,
maxUsernameLength: 30,
maxBioLength: 160,
}These limits are enforced consistently across profile management features and help prevent excessively large user-generated content.
The configuration also defines default values used when profile information is unavailable.
Upload limits are configured independently for each upload surface.
uploads: {
avatarSizeLimitInMB: 5,
blogImageSizeLimitInMB: 10,
}Separate limits allow user profile uploads and content-management uploads to evolve independently.
For example, a product might allow large blog images while keeping avatar uploads intentionally small.
Blog behavior is configured through the blog section.
blog: {
pageHeading: "...",
pageSubHeading: "...",
autoSaveIntervalMs: 1500,
}These settings control:
The autosave interval determines how frequently draft content is persisted while editing. For detailed implementation guidance, see the Blog documentation.
Notification settings control retention and delivery behavior.
notifications: {
retentionDays: 90,
pollingIntervalMs: 60000,
}These values influence:
When Ably is enabled, additional retry settings become available.
ably: {
enabled: true,
maxRetries: 3,
retryDelayMs: 2000,
}For detailed implementation guidance, see the Notifications documentation.
Background processing is controlled through the backgroundJobs section.
backgroundJobs: {
inngest: {
enabled: false,
retries: 3,
},
}This allows background processing infrastructure to be enabled or disabled without modifying application code. For detailed implementation guidance, see the Background Jobs documentation.
Legal contact information is configured centrally.
legal: {
grievance: {
officerName: "...",
designation: "...",
},
}These values populate legal and compliance-related pages throughout the application.
Organizations operating in jurisdictions with mandatory grievance handling or regulatory disclosure requirements should review this section carefully before launch. For detailed implementation guidance, see the Legal Pages documentation.
Documentation behavior is configured through the documentation section.
documentation: {
docsPath: "content/docs",
docsBaseUrl: "/docs",
}These values define:
Most projects will never need to modify these values, but they are available for teams with custom content architectures. For example, if you decided to move your app’s documentation to a separate subdomain like docs.acme.com, you would the update docsBaseUrl to /.
SEO-related settings are exposed through the seo section.
seo: {
verification: {
google: "",
bing: "",
},
}These values are commonly used for search-engine verification and webmaster tooling. The metadata configuration discussed earlier is also connected through this section. For detailed implementation guidance, see the SEO documentation.
Analytics behavior is controlled through configuration rather than code changes.
analytics: {
postHog: {
enabled: false,
capturePageActivity: false,
},
}This allows analytics collection to be enabled, disabled, or adjusted without modifying application logic.
For detailed implementation guidance, see the Analytics documentation.
Observability settings govern error tracking and structured logging.
observability: {
errorTracking: {},
logging: {},
}Error tracking configuration includes:
Logging configuration includes:
transportProvider: "console" as "console" | "axiom" | "betterstack",Supported providers currently include:
For complete observability guidance, see the Observability documentation.
Security controls are configured through the security section.
security: {
arcjet: {
enabled: false,
},
}This section contains rate-limit profiles and abuse-prevention controls for different application surfaces.
Examples include:
Each profile can be configured independently.
authRestrictive: {
max: 10,
interval: "10m",
}For detailed implementation guidance, see the Security documentation.
Audit-log retention behavior is configurable.
auditLogs: {
retentionDays: 90,
activityFeedLimit: 10,
}These values control:
Organizations with compliance requirements may need to adjust retention periods to match internal policies.
User presence behavior is configured through the activity section.
activity: {
heartbeatIntervalMs: 10 * 60 * 1000,
onlineThresholdMs: 15 * 60 * 1000,
}These values determine how the application calculates online and offline status.
The heartbeat interval controls how often clients report activity, while the threshold determines how long a user may remain inactive before being considered offline. The values for both heartbeatIntervalMs and onlineThresholdMs are in milliseconds.
Pagination defaults are configured centrally.
pagination: {
admin: {
usersPageSize: 10,
},
}These values define default page sizes for administrative and user-facing interfaces. Changing them affects how much data is loaded and displayed per request throughout the application. Larger values reduce pagination frequency but increase query and payload sizes, while smaller values improve responsiveness at the cost of more frequent page loads. That’s a trade-off worth keeping in mind while scaffolding your platform.
Many configuration values are documented alongside the features that consume them.
| Feature | Configuration file | Documentation |
|---|---|---|
| Billing | pricing.ts | Payments |
emails.ts | ||
| API Keys | api-keys.ts | API Keys |
| SEO | metadata.ts | SEO |
| Analytics | site.ts | Analytics |
| Observability | site.ts | Observability |
| Security | site.ts | Security |
| Notifications | site.ts | Notifications |
| Background Jobs | site.ts | Inngest |
When modifying a feature, consult its dedicated documentation before changing configuration values. This page serves as a map of the configuration system, while individual feature pages provide implementation-specific guidance.