AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Ktor Scaffold

skill-navikt-copilot-ktor-scaffold · by navikt

Scaffold eit nytt Ktor-prosjekt med Kotliquery, Flyway, Koin og Nais-konfigurasjon

No reviews yet
0 installs
21 views
0.0% view→install

Install

$ agentstack add skill-navikt-copilot-ktor-scaffold

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-navikt-copilot-ktor-scaffold)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Ktor Scaffold? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Ktor Project Scaffold

Scaffold a new Kotlin/Ktor service following Nav's standard patterns. This skill composes patterns from existing Nav skills and instructions.

Related Skills

Use these skills for deeper guidance on specific areas:

| Skill | When | |-------|------| | kotlin-app-config | Sealed class configuration pattern | | flyway-migration | Database migration scripts | | tokenx-auth | Service-to-service authentication | | observability-setup | Prometheus metrics and health endpoints | | api-design | REST API conventions and error handling |

Step 1: Project Structure

Generate this directory layout:

my-service/
├── build.gradle.kts
├── settings.gradle.kts
├── gradle/
│   └── libs.versions.toml
├── src/
│   ├── main/
│   │   ├── kotlin/no/nav/myservice/
│   │   │   ├── Application.kt
│   │   │   ├── Config.kt
│   │   │   ├── Routes.kt
│   │   │   ├── Database.kt
│   │   │   └── domain/
│   │   └── resources/
│   │       ├── db/migration/
│   │       │   └── V1__initial_schema.sql
│   │       └── logback.xml
│   └── test/
│       └── kotlin/no/nav/myservice/
│           └── ApplicationTest.kt
├── .nais/
│   ├── app.yaml
│   └── app-dev.yaml
└── Dockerfile

Step 2: Gradle Configuration

libs.versions.toml

[versions]
kotlin = "2.3.20"
ktor = "3.4.2"
kotliquery = "1.9.1"
hikaricp = "7.0.2"
flyway = "12.2.0"
postgresql = "42.7.10"
koin = "4.2.0"
logback = "1.5.32"
logstash = "9.0"
micrometer = "1.16.4"
kotest = "6.1.10"
mockk = "1.14.9"
testcontainers = "2.0.4"
mockOAuth2 = "3.0.1"

[libraries]
ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" }
ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor" }
ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktor" }
ktor-serialization-jackson = { module = "io.ktor:ktor-serialization-jackson", version.ref = "ktor" }
ktor-server-auth = { module = "io.ktor:ktor-server-auth", version.ref = "ktor" }
ktor-server-auth-jwt = { module = "io.ktor:ktor-server-auth-jwt", version.ref = "ktor" }
ktor-server-metrics-micrometer = { module = "io.ktor:ktor-server-metrics-micrometer", version.ref = "ktor" }
ktor-server-test-host = { module = "io.ktor:ktor-server-test-host", version.ref = "ktor" }

kotliquery = { module = "com.github.seratch:kotliquery", version.ref = "kotliquery" }
hikaricp = { module = "com.zaxxer:HikariCP", version.ref = "hikaricp" }
flyway-core = { module = "org.flywaydb:flyway-core", version.ref = "flyway" }
flyway-postgres = { module = "org.flywaydb:flyway-database-postgresql", version.ref = "flyway" }
postgresql = { module = "org.postgresql:postgresql", version.ref = "postgresql" }

koin-ktor = { module = "io.insert-koin:koin-ktor", version.ref = "koin" }
koin-logger-slf4j = { module = "io.insert-koin:koin-logger-slf4j", version.ref = "koin" }

logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
logstash-encoder = { module = "net.logstash.logback:logstash-logback-encoder", version.ref = "logstash" }
micrometer-prometheus = { module = "io.micrometer:micrometer-registry-prometheus", version.ref = "micrometer" }

kotest-runner = { module = "io.kotest:kotest-runner-junit5", version.ref = "kotest" }
kotest-assertions = { module = "io.kotest:kotest-assertions-core", version.ref = "kotest" }
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
testcontainers-postgres = { module = "org.testcontainers:postgresql", version.ref = "testcontainers" }
mock-oauth2-server = { module = "no.nav.security:mock-oauth2-server", version.ref = "mockOAuth2" }

build.gradle.kts

plugins {
    kotlin("jvm") version "2.1.10"
    id("com.gradleup.shadow") version "9.0.0-beta6"
}

application {
    mainClass.set("no.nav.myservice.ApplicationKt")
}

dependencies {
    implementation(libs.ktor.server.core)
    implementation(libs.ktor.server.netty)
    implementation(libs.ktor.server.content.negotiation)
    implementation(libs.ktor.serialization.jackson)
    implementation(libs.ktor.server.auth)
    implementation(libs.ktor.server.auth.jwt)
    implementation(libs.ktor.server.metrics.micrometer)

    implementation(libs.kotliquery)
    implementation(libs.hikaricp)
    implementation(libs.flyway.core)
    implementation(libs.flyway.postgres)
    runtimeOnly(libs.postgresql)

    implementation(libs.koin.ktor)
    implementation(libs.koin.logger.slf4j)

    implementation(libs.logback.classic)
    implementation(libs.logstash.encoder)
    implementation(libs.micrometer.prometheus)

    testImplementation(libs.ktor.server.test.host)
    testImplementation(libs.kotest.runner)
    testImplementation(libs.kotest.assertions)
    testImplementation(libs.mockk)
    testImplementation(libs.testcontainers.postgres)
    testImplementation(libs.mock.oauth2.server)
}

tasks.test {
    useJUnitPlatform()
}

Step 3: Application.kt

package no.nav.myservice

import io.ktor.server.engine.*
import io.ktor.server.netty.*
import org.koin.ktor.plugin.Koin

fun main() {
    val config = Config.from(System.getenv())

    Database.migrate(config.database)

    embeddedServer(Netty, port = 8080) {
        install(Koin) {
            modules(appModule(config))
        }
        configureRouting()
        configureSerialization()
        configureMonitoring()
    }.start(wait = true)
}

Step 4: Config.kt

Use the sealed class pattern from kotlin-app-config skill:

package no.nav.myservice

sealed class Config(
    val database: DatabaseConfig,
) {
    data class Dev(private val env: Map) : Config(
        database = DatabaseConfig(
            url = env.getValue("DATABASE_URL"),
            username = env.getValue("DATABASE_USERNAME"),
            password = env.getValue("DATABASE_PASSWORD"),
        ),
    )

    data class Prod(private val env: Map) : Config(
        database = DatabaseConfig(
            url = env.getValue("DATABASE_URL"),
            username = env.getValue("DATABASE_USERNAME"),
            password = env.getValue("DATABASE_PASSWORD"),
        ),
    )

    data object Local : Config(
        database = DatabaseConfig(
            url = "jdbc:postgresql://localhost:5432/myservice",
            username = "postgres",
            password = "postgres",
        ),
    )

    companion object {
        fun from(env: Map): Config = when (env["NAIS_CLUSTER_NAME"]) {
            "dev-gcp" -> Dev(env)
            "prod-gcp" -> Prod(env)
            else -> Local
        }
    }
}

data class DatabaseConfig(val url: String, val username: String = "", val password: String = "")

Step 5: Database.kt

package no.nav.myservice

import com.zaxxer.hikari.HikariDataSource
import kotliquery.queryOf
import kotliquery.sessionOf
import kotliquery.using
import org.flywaydb.core.Flyway
import javax.sql.DataSource

object Database {
    fun dataSource(config: DatabaseConfig): HikariDataSource = HikariDataSource().apply {
        jdbcUrl = config.url
        username = config.username
        password = config.password
        maximumPoolSize = 10
        minimumIdle = 2
    }

    fun migrate(config: DatabaseConfig) {
        Flyway.configure()
            .dataSource(config.url, config.username, config.password)
            .load()
            .migrate()
    }
}

Step 6: Dockerfile

FROM gradle:8-jdk21 AS build
WORKDIR /app
COPY build.gradle.kts settings.gradle.kts ./
COPY gradle ./gradle
RUN gradle dependencies --no-daemon
COPY src ./src
RUN gradle shadowJar --no-daemon

FROM europe-north1-docker.pkg.dev/cgr-nav/pull-through/nav.no/jre:openjdk-21
WORKDIR /app
COPY --from=build /app/build/libs/*-all.jar app.jar
CMD ["-jar", "app.jar"]

Step 7: NAIS Manifest

Generate using the nais-manifest prompt, ensuring:

  • PostgreSQL via gcp.sqlInstances
  • Prometheus enabled at /metrics
  • Health endpoints at /isalive and /isready

Verification

After scaffolding, run:

./gradlew build
./gradlew test

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

  • Author: navikt
  • Source: navikt/copilot
  • License: MIT
  • Homepage: https://ki-utvikling.nav.no/nav-pilot

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.